15 Apr 1998: SREF_QUEUE: Working with SRE-http queues. Abstract: SREF_QUEUE is an SRE-http "macrospace" procedure that allows you to work with "SRE-http queues". These queues provide a convenient, queue-like means of storing and sharing data in a globally accessible, semi-permanent location. Table of Contents I. Introduction II. The SREF_QUEUE procedure II.a SREF_QUEUE actions II.a.1 POP and READ II.a.1.i The TYPE modifer II.a.1.ii The INFOFIELD modifer II.a.1.iii Examples II.b FIND II.c PUSH and QUEUE III. The INIT, KILL, LOCK and INFO actions III.a INIT III.b KILL III.c INFO III.d LOCK IV. Examples V. Using SREF_QUEUE with a dedicated daemon Appendix 1. SREF_QUEUE error codes -------------------- I. Introduction SRE-http (as of ver 1.2L) supports a flexible form of "queue". The primary purpose of these SRE-http queues is to store and retrieve information in a globally accessible, semi-permanent location. In many ways, they are functionally similar to REXX queues, but are much more easily manipulated. SREF_QUEUE "macrospace" procedure is primarily meant to be used by SRE-http addons that have light to moderate needs (for passing and storing data). If you have more substantial needs, you might want to use regular REXX queues, or create an SRE-http daemon (see DAEMONS.DOC for a discussion of how to work with SRE-http daemons). Another interesting possiblity is the SRE-Data addon; which is designed to perform fast lookup in fairly large, static datasets. To support these queues, SRE-http uses its "variable storage daemon". This introduces a few drawbacks: > when GoServe shuts down, SRE-http queues disappear. > The variable storage daemon services a number of SRE-http needs; if an SRE-http queue is heavily used, other SRE-http functions will be impacted. The latter problem can be partially overcome by starting multiple "variable storage" daemons. This does require some special configuration; which is discussed in section V below. -------------------- II. SREF_QUEUE The SREF_QUEUE procedure is used to access and manipulate SRE-http queues. SREF_QUEUE is called as: retvalue=SREF_QUEUE(queueName,action,value,port) where: queuename: The name of a queue; it can be any REXX-style, variable name. Queuename can also contain a daemon identifier, in which case quenamename will be of the form: name.daemon_id action: Type of action. and list of modifier. Valid actions include: INIT, INFO, LOCK, PUSH, POP, READ, FIND, and KILL. value: The record to be added (used with PUSH, FIND, and INFO) port: Used when SREF_QUEUE is called from an external process (i.e; not from a thread or child-process of GoServe). Port should be the port that GoServe/SRE-http is running under (typically, port=80). The action parameter consists of an action, plus an optional list of modifiers. The POP, READ, FIND, QUEUE and PUSH actions are described in section II.a to II.c. Section III discusses the KILL, INIT, LOCK and INFO actions; and section IV contains a simple example. Summarizing what follows, to use SREF_QUEUE you should: 1) Initialize a queue using INIT. 2) Write to a queue using PUSH and QUEUE. Read from a queue using FIND, POP or READ. Administer a queue using the LOCK and INFO 3) Stop a queue using the KILL action -------------------- II.a: The action parameter: The action is used to specify what you want to do with an SRE-http queue. It consists of two components: the action, and a list of modifiers. There are five read/write actions: POP, READ, FIND, QUEUE, and PUSH There are four administrative actions: INIT, INFO, LOCK, and KILL. The modifiers (there can be several) depend on the action. Notes: * At its simplest, without specifing modifiers, using POP and QUEUE will create a LIFO (last in,first out) queue. * The actions and modifiers should be seperated by spaces. The "action" should ALWAYS be first, but the modifiers can appear in any order. * Several of the actions and modifiers can contain an = sign, followed by a value. This = sign should NOT be surrounded by spaces. For example: REC=19 is okay, REC= 19 is bad. For most of these actions, a string (text or binary), or a numeric count, will be returned. If an error occurs, either an empty string or a negative valued error code will be returned. Appendix 1 summarizes the error codes. II.a.1. POP and READ POP will "read and remove" a record, while READ will "read and retain" a record. Otherwise (with a few small exceptions) they are similar. You can POP (or READ) from the top of a queue, the bottom of a queue. You can also POP (or READ) multiple records; either explicitily specified, or based on a timespan. POP and READ can have two modifiers: a TYPE and an INFOFIELD. TYPE: The location and size of the POP or READ. Valid TYPEs are: TOP, BOTTOM, TOP=nnn, BOTTOM=nnn, NEWEST=ttt, OLDEST=ttt, and REC=nnn INFOFIELD: The information to be returned. Valid INFOFIELDs are: VALUE, TIME, SIZE, NOREAD, and #RECORDS Note that the default TYPE is TOP, and the default INFOTYPE is VALUE. Also, REC=nnn can only be used with READ. II.a.1.i: The TYPE modifier The TYPE modifiers have the following effects: TOP : Read one record from the top of the queue BOTTOM: Read one record from the bottom of the queue TOP=nnn : Read nnn records from the top of the queue. For example, TOP=3 will read (and remove) the top 3 records. Note that TOP=1 and TOP have the same effect BOTTOM=nnn: Read nnn records from the bottom of the queue Note that BOTTOM=1 and BOTTOM have the same effect NEWEST=ttt: Similar to TOP=nnn, but ttt refers to a time offset. Thus, records written (to the top) within ttt (of the current time) will be read (and removed). ttt should contain a value and a time unit: where the case- insensitive time unit is D,H,M or S (for days, hours, minutes or seconds). For example: 0.042D=1H=60M=3600S. OLDEST=ttt : Similar to NEWEST=ttt, but from the bottom of the queue, and using the "queue creation time" as the reference time. REC=nnn : Read from the nnn'th record: from the top if nnn<0 or from the bottom if nnn>0. For example: READ REC=-1 is the same as READ TOP READ REC=-2 will read the 2nd record from the top READ REC=1 is the same as READ BOTTOM Removing versus Retaining: POP will read and REMOVE records; while READ will read and RETAIN records. Thus, multiple calls to READ will return the same record (say, the top record), while multiple calls to POP will return progressively older records (until the queue is empty). To READ progressively older records (without removing), you can increase (or decrease) the nnn in a series of READ REC=nnn calls. That is, use READ REC=-1, READ REC=-2, etc. Note that you may want to LOCK the queue first (see section III for a discussion of queue LOCKing). Multiple record returns: For multiple record returns, each record will be delimited by a CRLF ('0d0a'x). If your records are multi-line records, or contain binary information, this will cause problems. You can work around this by setting an alternate DELIMiter when you INITialize No data results: > If the SRE-http queue is empty, an empty string or a -3 error code is returned. > If the NEWEST (or OLDEST) record is older (newer) then specified (as set by the value), then an empty string is returned (or a -9 error code). Multiple record READs and POPs: > By default, when a "multiple record request" is made (such as TOP=5), then CRLFs ('0d0a'x) are used to seperate records when. For binary records, or for multiple-line records, this will clearly cause problems. You can work-around this dilemna by setting a queue-specific DELIMiter when you INITialize. > If you ask for more records then exist, all records will be returned (you can use the INFO action to get the length of an SRE-http queue). > For TOP or NEWEST: the topmost records are written first For BOTTOM or OLDEST: the bottommost records are written first > To extract records from the value returned, you can use: mm=0 ; dlm='0d0a'x do until value="" parse var value aline (dlm) value mm=mm+1; stuff.mm=aline end II.a.1.ii: The INFOFIELD modifier You can specify a VALUE, TIME, SIZE, #RECORDS or NOREAD INFOFIELD modifiers. These effect what information is returned. Bot READ and POP recognize the same INFOFIELD delimiters: VALUE : Return the value of the record(s). Note that records may be of any length, and may be text or binary. ID : Return the value of the ID field. TIME : The time the record was written,as an absolute date. For example: 13:33:51 21 Nov 1997 will yield 729348.56517 SIZE : Size of the record, in bytes #RECORDS : A count of the number of records that would be returned. This is designed to be used with READ NEWEST and READ OLDEST -- it tells you how many records are newer (or older) then the specified timespan. And if used with POP, it's a quick way of removing too new (or too old) records. NOREAD : Used with POP. POP the record(s), but don't bother returning any information. This speeds up removal of records from the queue, since SREF_QUEUE won't wait for a status message to be returned from the storage daemon. However, this means that several errors will not be reported; thus, NOREAD should be used with some caution. A Shortcut: You can use STRIP as a short hand for POP NOREAD. II.a.1.iii Examples of POP and READ a=sref_queue('myqueue','POP TOP') a=sref_queue('myqueue','POP TOP VALUE ') -- this is the same as POP TOP a=sref_queue('myqueue','POP NEWEST=30s') a=sref_queue('myqueue','POP BOTTOM=6 ') a=sref_queue('myqueue','POP TOP TIME') a=sref_queue('myqueue','READ REC=1') a=sref_queue('myqueue','READ BOTTOM REC=3') a=sref_queue('myqueue','READ OLDEST=1h #RECORDS') a=sref_queue('myqueue','READ TOP=5 SIZE ') -- 5 sizes, seperated by CRLFS a=sref_queue('myqueue','POP NOREAD TOP ') -- removes top record a=sref_queue('myqueue','STRIP') -- same as POP TOP NOREAD II.a.2: FIND FIND is used to search the queue for a match. By default, it will search the "ID" field (that can be optionally set when you PUSH or QUEUE a record). However, it can also be used to search the "values" of the records (but the search will be slower). FIND can have a TYPE, ID, MATCH, and INFOFIELD modifiers. You MUST also specify a "value" parameter containing the string (text or binary) to search for. Based on the TYPE and INFOFIELD modifiers, the first match will be returned. The valid TYPE modifiers are: TOP : Start search from the top of the queue. TOP=nnn: Start search from the nnn'th record (inclusive), where nnn=1 means "top of the queue". BOTTOM: Start search from the bottom of the queue. BOTTOM=nnn: Start search from the nnn'th record (inclusive), where nnn=1 means "bottom of the queue". Note that if the TYPE modifier is not specified, TOP is assumed. The ID modifier is used to select whether to search the ID or "value" fields of the record. ID=YES : search the ID field ID=NO : search the "value" of the record By default, ID=YES is used. Note that when searching the ID, a case insensitive search is used. When searching the "value" field, a case sensitive search is used. The MATCH modifier is used to set what kind of search to perform. MATCH=EXACT : Exact match. MATCH=PARTIAL : Case insensitive partial match -- can match any substring (note that ID searchs are always case insensitive). MATCH=WILD : Wildcard match -- * characters will be used to do a wildcard match. Note that the default is EXACT. Although WILD= is fairly powerful (you can have multiple *s), it's somewhat slower; in many cases PARTIAL can be used instead. The INFOFIELD modifier dictates what information should be returned (see the descriptions of the READ and POP INFOTYPE modifier for further details): VALUE: Return the value of the record. TIME: The time the record was written,as an absolute date. SIZE: Size of the record, in bytes ID: Id of the field REC: Record number of the match (from the bottom if READ BOTTOM, otherwise from the TOP). Can be used with READ REC= to directly query the record (after you've found a match), or as a base for another search (i.e.; add 1 and use in TOP=nnn). Note that if the INFOFIELD modifier is not specified, VALUE is assumed. The "value" parameter should contain the string to search for. This string will be compared against the ID field, or the "value" field, of each record, with the first match returned. Examples: a=sref_queue('myqueue','FIND','MY_DAD ') a=sref_queue('myqueue','FIND BOTTOM TIME ID=NO MATCH=PARTIAL ','chevy') a=sref_queue('myqueue','FIND BOTTOM=10 TIME ','WILD=*TRUCK*') II.a.3: PUSH and QUEUE PUSH and QUEUE are used to place records on the queue. You can add a record to the top or bottom of the queue. The action parameter consists of a TYPE and several OPTIONs. With PUSH the TYPES are TOP, BOTTOM and REC=nnn. The OPTIONS include QUICK, REMOVE, and ID. QUEUE is actually a shorthand for PUSH BOTTOM -- hence, no TYPE is needed. In addition to the action, you must specify a value parameter. The value parameter can be any text or binary string. TYPEs for PUSH: TOP: Write a record to the top of the queue. BOTTOM: Write a record on the bottom of the queue. Note that QUEUE = PUSH BOTTOM REC=nnn: Write to record nnn; where nnn=1 is the bottom, and nnn=-1 is the top. Note that if a PUSH actions has no TYPE modifier, TOP is assumed. A TYPE should never be used with QUEUE -- QUEUE always means PUSH BOTTOM! OPTIONS for PUSH and QUEUE: QUICK: Do not wait for a response from the variable storage queue. This speeds things up a bit, but is somewhat riskier (since errors are more likely to be undetected). REMOVE: If the queue currently has its maximum value of records (as specified by the INIT action) remove a record and then write this record. If REMOVE is not specified, a "can't write new record" error is returned. The record removed will be at the "other end" of the queue. Thus, PUSH TOP REMOVE may cause the bottom record to be removed; while PUSH BOTTOM REMOVE (or QUEUE REMOVE) may cause the top record to be removed. ID=xxx: Set the ID field for this record with value xxx. xxx can be any length text string, but can not contain spaces or CRLFs. We recommend keeping it relatively short (say, less than 30 characters). The ID field is used with the POP and READ "ID" INFOTYPE, and can be searched by the FIND action. Caution concerning PUSH REC=nnn As with READ REC=nnn, the sign of nnn determines whether the record is determined from the top or bottom of the queue. As a relative number (from the top or bottom), additions/subtractions from the queue will change a record's REC number. This can cause unexpected problems in cases where multiple threads have access to the queue. Therefore, you may want to LOCK the queue before using PUSH REC=nnn and READ REC=nnn. Time of Creation: When you PUSH or QUEUE stores a record, the current time is also stored (accurate to about 1 second). READ NEWEST=ttt and POP NEWEST=ttt use this to selectively return a set of records -- all records stored within a specified timespan will be returned. Examples: a=srefqueue('myq','PUSH TOP', ' this is a record on the top') a=srefqueue('myq','QUEUE ',' this is a bottom record') a=srefqueue('myq','PUSH TOP ID=MY_DAD',birthday) a=srefqueue('myq','PUSH BOTTOM QUICK','store me, and move on ') a=srefqueue('myq','PUSH REMOVE',' store me, and make room ') a=srefqueue('myq','PUSH REC=10 ID=TENTH_VICTIM ',' replace the 10th record') -------------------- III. INIT, KILL, INFO and LOCK III.a. INIT The INIT action is used to initialize a queue, and to set a few INIT_OPTIONS. The INIT_OPTIONS should be set in the "value" parameter; and may include any or all of the following: SIZE=nnn : Maximum size of the queue (in records). Thus, SIZE=100 means "no more then 100 records". SIZE=0 means "no limit" LIFESPAN=ttt : Clear out records with that are older then lifespan. As with other SREF_QUEUE timespans, you can specify days, hours, minutes or seconds (i.e.; 1D=24M=3600M). LIFESPAN=0 means "indefinite lifespan". Caution: To save time, LIFESPAN will only check from the bottom of the queue up; and stop when it hits a "still alive" record. Thus, LIFESPAN should NOT be used for queues that will have records added to the bottom! DELIM=ppp : Specify an alternate delimiter to use to seperate records on multiple-record returns. By default, a CRLF ('0d0a'x) is used. If you have multiple-line records, or binary records, this can cause problems. The use of an alternate "delimiter" can often offer a workaround (so long as you know that the alternate delimiter is never used in the records!). Note that ppp MUST be a "packed 64" string. You can use SREF_MKPACK64 to create such a string. For example: mydelim='0d0a'x||' &&%**%&& '||'0d0a'x ppp=mk_pack64(mydelim) foo=sref_queue('ourqueue','INIT ','DELIM='||ppp) and then use mydelim when parsing multiple line returns (that is, do NOT use ppp; ppp is only used to transfer information to SREF_QUEUE). By default, SIZE=0, LIFESPAN=0 and DLM='0d0a'x Examples: Set up an unlimited length queue named 'myqueue_1' status=SREF_QUEUE('myqueue_1','INIT') Set up an 100 record (maximum) queue with the name 'his_queue', and automatically remove records older then 1 hour. status=SREF_QUEUE('his_queue','INIT',' size=100 lifespan=1h ') The returned value (status) will be 1 if success, and a negative number if an error occured. Error codes include: -5 : Queuename already exists -10 : Queue not specified -187: System error. Notes: * When a maximum is hit... If REMOVE is not a PUSH (or QUEUE) OPTION: then additional records will not be accepted -- and a -8 error code is returned. You'll have to POP open a space on the queue. If REMOVE is a PUSH OPTION then a record will be removed from the "other end" -- for example: for PUSH TOP, a record will be removed from the bottom end (the new record being added to the top). * If you write to a queue without initializing, a new queue will be created with unlimited length, infinite lifespan, and a CRLF delimiter. * When you INITialize a queue, the "creation time" is recorded. The "creation time" is used with READ OLDEST. Note that the creation time can not be changed (short of KILLing and reINITializing the queue). * Removal of "older then lifespan" records occurs after every PUSH (or QUEUE). As noted above, a "work from the bottom up until you hit a live record" algorithim is used -- thus, if you add records to the bottom of a queue, LIFESPAN will have unexpected consequences. -------------------- III.b KILL KILL is used to end a queue. Use KILL to save resources. Example: status=sref_queue('myqueue','KILL') will kill the myqueue SRE-http queue. The returned value will equal the number of records that were in the queue. If an error occurs, a negative value will be returned. Error codes include: -1 : No such queuename -10 : Queue not specified Note that anyone call kill an SRE-http queue -- SRE-http queues are not "owned" ! -------------------- III.c INFO INFO is used to obtain info about the SRE-http queue. The value parameter is used to specify what to get. Valid values are: RECORDS : # of records in queue; 0 if none. SIZE : Total storage, in bytes. 0 if no records, or if all empty records. CREATION : Time of creation, in "julian" format. For example: 13:33:51 21 Nov 1997 will yield 729348.56517 MAX : Maximum size (0 if unlimited) LOCK : Return LOCKING status. If not locked, or if the LOCK has expire, a 0 is returned. Otherwise, the current key is returned (note that the default key is "1"). There several "summary of all queues" options. To uses these, queuename should be * (or *.daemon_id). These "summary options" are: #QUEUES : Number of queues SIZE : Size (in bytes) of all queues NAMES : Names of all the queues (in a space delimited list) A negative returned value is used as an error code, including: -1 : No such queuename -4 : No such value parameter -10 : Queue not specified Examples: To find the current number of records in the myqueue: ngot=sref_queue('myqueue','INFO','Records') To find the maximum size of myqueue: maxsize=sref_queue('myqueue','info','max') To list all currnently active queues: nqueues=sref_queue('*','INFO','NAMES') III.d LOCK The LOCK action is used to lock a queue; which means that records can not be added or removed. There are two variants of LOCK. LOCK ON=aaa : Lock a queue, with a key of aaa and LOCK OFF=aaa : Unlock a queue, using the aaa key. The "aaa" key is optional; if not specified, a value of 1 is used. Key's can be used to help independent processes know "who has locked the queue". For example: LOCK ON : Lock the queue, using the default key (1). LOCK ON=T1 : Lock the queue, using a key of T1 LOCK OFF=T1 : Unlock the queue IF it was locked with a key of T1 LOCK OFF : Unlock the queue, regardless of what the key is. Thus, LOCK OFF is universal -- which means that anyone can unlock a queue! When you LOCK a queue you can also specify, in the value parameter, a timspan; after this timepspan the queue will be unlocked. If not specified, the queue will be locked indefinitely. As with other SREF_QUEUE timespans (such as in POP NEWEST=ttt), a (case insensitive) D,H,M, or S unit should be the last character of the timespan. For example, six hours can be spedified as: 0.25D, 6H, 360M, or 21600s Queue locking is most useful when used prior to a READ REC= and PUSH REC= (since it prevents additions to, and removals from, the queue; relative positions will be stable). However, should a thread that locks a queue fail/forget to unlock it, the queue will remain locked until the timespan expires (perhaps indefinitely), or until some other thread decides to unlock it (note that anyone can LOCK or UNLOCK a queue). As mentioned above, in highly multi-threaded environments, the KEY can be used to help keep track of who's "locked" the queue. LOCK will return a 1 for success (either locked or unlocked), or a negatively valued error code: -1 : No such queuename -10 : Queue not specified -15 : Mismatched key (you can use INFO to find the current key) -14 : Queue already locked Examples: astat=sref_queue('myqueue','LOCK ON','10s') astat=sref_queue('myqueue','LOCK ON=T1') astat=sref_queue('myqueue','LOCK OFF=T2') astat=sref_queue('myqueue','LOCK OFF') -------------------- IV: An Example **** The following is an SRE-http addon that provides a useless demonstration: parse arg ddir, tempfile myq='myqueue' a1=sref_queue(myq,'INIT 100') /* initialize a queue, max of 100 records */ call lineout tempfile,' Sample results ' call lineout tempfile,' ' if a1<0 then call lineout tempfile,' Warning: queue exists ' call lineout tempfile,' ' call lineout tempfile, ' loading ... ' aa=sref_queue(myq,'PUSH ','This is line 1') call lineout tempfile," PUSH result= "aa aa=sref_queue(myq,'PUSH ID=GREAT_1 ','This is line 2 ') call lineout tempfile,' PUSH ID result = 'aa aa=sref_queue(myq,'QUEUE ID=BOT_ONE ','This is line zero') call lineout queuefile', QUEUE result= ' aa call lineout tempfile, ' ' call lineout tempfile, ' retrieving ... ' in=sref_queue(myq,'INFO','SIZE') call lineout tempfile,' INFO SIZE='in w1=sref_queue(myq,'FIND BOTTOM ','GREAT_1') call lineout tempfile,' FIND ID = ' w1 b1=sref_queue(myq,'POP') b2a=sref_queue(myq,'READ BOTTOM SIZE') b2b=sref_queue(myq,'READ BOTTOM') b2ba=sref_queue(myq,'READ BOTTOM SIZE') b2bb=sref_queue(myq,'READ BOTTOM TIME') b2bc=sref_queue(myq,'READ BOTTOM ID') b3=sref_queue(myq,'POP top=2') b4=sref_queue(myq,'READ') call lineout tempfile,' POP result=' b1 call lineout tempfile,' READ BOTTOM SIZE=' b2a call lineout tempfile,' READ BOTTOM (val,size,time,id) = ' b2b', 'b2ba', 'b2bb', 'b2bc call lineout tempfile,' POP top=2 = ' b3 call lineout tempfile,' READ = ' b4 call lineout tempfile 'FILE ERASE type text/plain name ' tempfile return 'test queue ' **** Will return: Sample results Warning: queue exists loading ... PUSH result= 1 PUSH ID result = 1 retrieving ... INFO SIZE=45 FIND ID = This is line 2 POP result= This is line 2 READ BOTTOM SIZE= 17 READ BOTTOM (val,size,time,id) = This is line zero, 17, 729352.04117, BOT_ONE POP top=2 = This is line 1 This is line zero READ = -------------------- V: Running SREF_QUEUE with a dedicated daemon As mentioned in the introduction, SREF_QUEUE uses the SRE-http "variable storage daemon" to store SRE-http queues. This daemon also provides a number of other services to SRE-http, such as "variable storage" and storage for the "hit cache". Thus, heavy use of SREF_QUEUE (such as extensive ID searches) may adversely impact the performance of other SRE-http functions; hence reponse time for other requests (that have nothing to do with the SRE-http queues). In order to avoid some of these problems, you may want to use a seperate "daemon" for SRE-http queue storage. In fact, you can even generate several queue storage daemons, with each of these daemons handling a subset of the SRE-http queues (in the extreme, you could have one daemon per SRE-http queue). To do this requires two steps: 1) Changing the STORAGE_DAEMONS parameter 2) Modifying the queuename argument The STORAGE_DAEMONS parameter (which is set in SREFMON.CMD) controls how many instances of the SRE-http "variable storage" daemon are launched. By default (STORAGE_DAEMONS=1) a single instance is launched. Increasing this parameter will increase the number of "variable storage daemons" launched. That is, when STORAGE_DAEMONS=3, then 3 instances of this daemon are launched. Note that these daemons are functionally independent -- they do NOT share data storage. Thus, when using an particular SRE-http queue, you have to decide which "instance" of the daemon to use, and stick with this decision. Given that multiple storage daemons are avaiable, you can dictate which one to use by appending (after a .) a daemon_id to the end of the queuename. The daemon_id is simply a number between 1 and STORAGE_DAEMONS. Example: to initialize the "myqueue" SRE-http queue under the number 2 daemon, and then PUSH a record: stat=sref_queue('myqueue.2','INIT') if stat>0 then do stat2=sref_queue('myqueue.2','PUSH','Hello world') end Notes: * queuename.1 is the same as queuename -- the "number 1 daemon" is always the default daemon. * if you specify a non-existent daemon id, an error will occur; with either an empty string returned, or a -187 error code. -------------------- Appendix 1. SREF_QUEUE Error codes Error codes will be returned when some kind of error occurs. Their values will depend on what action was requested. action=READ, FIND or POP If INFOFIELD=VALUE For all errors, an empty string is returned. If INFOFIELD=SIZE, ID, TIME, RECNUM -1 = no such queuename -2 = no such record (only used with READ REC) -3 = empty queue -8 = bad value -9 = no records within selected timespan (used with NEWEST and OLDEST) -10 = queue name not specified -11 = no id specified (only used with FIND) -12 = no id match (only used with FIND) -16 = FIND: offset > then # records in queue -20 = queue locked (only used with POP) -100 = no such modifier (2nd word not OLDEST NEWEST TOP or BOTTOM) action=INIT -5 = queuename in use -6 = bad value for maximum size -10 = queuename not specified action=KILL or INFO -1 = no such queuename -4 = no such INFO value (not RECORDS CREATION MAX SIZE #QUEUES NAMES) -10 = queuename not specified action=LOCK -1 : No such queuename -10 : Queue not specified -15 : Mismatched key -14 : Queue already locked action=PUSH -1 = no such queuename -2 = no such record (used with PUSH REC) -8 = no room left on queue -10 = queuename not specified -20 = queue locked For all actions: System errors cause a -187 to be returned. System error include: specifying a non-existent daemon_id that could not create semaphore (used to communicate with daemon) could not use semaphore problem communication with daemon Note that when a system error occurs, error messages will be displayed in the pmprintf window.