SemLoad
call RxFuncAdd 'SemLoad', 'RexxSem', 'SemLoad' call SemLoad /* this will load all the routines */
SemDrop
call SemDrop /* doesn't return anything */
SemVersion
parse value SemVersion() using 'REXXSEM v' major '.' minor ',' . if \datatype(right(minor, 1), 'N') then do betalevel = right(minor, 1) minor = left(minor, length(minor) - 1) endMost of the time, however, you won't need to know the version - just to check that the right version is the one you think you're using. (Ever have the same DLL on your system twice?)
SemLogState
call SemLogState 'my.log.file'The my.log.file will be the file to log all the information to. The most important thing would be to ensure that you are using SemMClose/SemEClose on all available semaphores before your script ends as this may block a lot of other threads if you forget! The logfile will tell you which semhandles have not been closed for this purpose.
If all semhandles are closed, the logfile will not be created.
See: SemMLogState
,
SemELogState
.
This function simply does the job of both of these.
SemSetLanguageDll
call SemSetLanguageDll 'rxsem_de' /* German! */
SemReturnValidAlways
call SemReturnValidAlways 'YES' /* never aborts script - debug mode */ call SemReturnValidAlways 'NO' /* always abort script on error */Note, however, that timing out on a timed claim is _not_ an error, and will never abort the script.
Whenever an error occurs, the function will always return a string that starts with 'ERROR:'. The rest of the string will contain an explanation of the error.
SemMCreate32 (was SemCreateMutex)
semhandle = SemMCreate32("\sem32\some\semaphore.sem")Note that if the semaphore is already "created" (for example, by another process), this will simply open it and return the handle for that. Also, even though the system will accept /sem32/blah/blah, this rexx function will not. This may be addressed in future versions. The system does not see a difference, however, between /sem32/blah/blah and \sem32\blah\blah. Since translating "/" to "\" is such an easy thing to do in REXX, it's pretty low priority to change.
Also note that mutex and event semaphores cannot share the same name.
SemMCreate16 (was SemCreateSem16)
semhandle = SemMCreate16("\sem\squish\default")Note that if the semaphore is already "created" (for example, by another process), this will simply open it and return the handle for that. Also, even though the system will accept /sem/blah/blah, this rexx function will not. This may be addressed in future versions. The system does not see a difference, however, between /sem/blah/blah and \sem\blah\blah. Since translating "/" to "\" is such an easy thing to do in REXX, it's pretty low priority to change.
Also note that mutex and event semaphores cannot share the same name. Not that this is a big deal because REXXSEM doesn't support 16-bit event semaphores...
SemMCreate
semhandle = SemMCreate("\sem\squish\default") semhandle = SemMCreate("\sem32\another\semaphore")Note that if the semaphore is already "created" (for example, by another process), this will simply open it and return the handle for that. Also, even though the system will accept /sem/blah/blah, this rexx function will not. This may be addressed in future versions. The system does not see a difference, however, between /sem/blah/blah and \sem\blah\blah. Since translating "/" to "\" is such an easy thing to do in REXX, it's pretty low priority to change.
Also note that mutex and event semaphores cannot share the same name.
SemMClaim
rc = SemMClaim(semhandle, 150) rc = SemMClaim(semhandle) rc = SemMClaim(semhandle, ,'Waiting for \sem32\somesemaphore.sem')The semhandle can be any mutex semhandle created by this library. Whether it is a Sem16 or a Mutex semaphore does not matter. If you want to change whether you use a 16-bit or 32-bit semaphore, you only have to change the create function, but nothing else. If the semhandle is not a valid pending handle, this will abort your script.
The optional second parameter is a timeout in milliseconds that if the semaphore is not available within, the failed return value ("TIMEOUT") will be returned. If this is not given, this to means indefinite (wait forever).
The optional third parameter is a message to display. The message will be displayed (with a newline) to the user of your rexx script (on stdout) to alert the user that the semaphore is being waited on if it has already been claimed. It will then proceed to wait for the timeout. If the semaphore has not been claimed by another thread or process, the message will not be displayed.
If the timeout passes without the semaphore becoming available, rc will be "TIMEOUT", otherwise it will be "CLAIMED".
SemMRelease
call SemMRelease semhandleThe semhandle can be any semhandle created by this library. Whether it is a Sem16 or a Mutex semaphore does not matter. If you want to change whether you use a 16-bit or 32-bit semaphore, you only have to change the create function, but nothing else. If the semhandle is not a valid pending handle, this will abort your script.
There is never a value returned.
SemMClose
call SemMClose semhandleThe semhandle can be any mutex semhandle created by this library. Whether it is a Sem16 or a Mutex semaphore does not matter. If you want to change whether you use a 16-bit or 32-bit semaphore, you only have to change the create function, but nothing else. If the semhandle is not a valid pending handle, this will abort your script.
The semhandle is no longer a valid pending handle after this function is called.
SemMCloseAll
call SemMCloseAllThis will force all mutex semaphores created in this process to be released and closed. No semaphore handle is valid after this. Semaphores created by other processes using this DLL are still valid.
SemMLogState
call SemMLogState 'my.log.file'The my.log.file will be the file to log all the information to. The most important thing would be to ensure that you are using SemMClose on all available semaphores before your script ends as this may block a lot of other threads if you forget! The logfile will tell you which semhandles have not been closed for this purpose.
If all semhandles are closed, the logfile will not be created.
SemMGetAllSems
call SemMGetAllSems 'sems.' do i = 1 to sems.0 say 'MutexSem #'i 'is' sems.i call SemMClose sems.i say 'Woops, make that *was*' sems.i'. It''s closed now.' endNote that if you do not include the '.' at the end of the name, it will be very difficult to loop through them. RXSEM will not put the dot on the end for you.
There is one, somewhat minor, exception to the no dot issue. A subarray is created with the name that was given to each semaphore. For example, if you created the semaphore with "\SEM32\mysem", and this is the first semaphore you created, the following code:
drop name mysem = SemMCreate('\SEM32\mysem') call SemMGetAllSems 'sems.' do i = 1 to sems.0 say 'MutexSem #'i 'is opened as' sems.i.name endwould have an output similar to:
MutexSem #1 is opened as \SEM32\mysem
SemMQuerySemHandle
This is likely to be a "slow" operation. Everything is internally stored in hash tables, but indexed on handle, not name.
Sample usage:
semh = SemMQuerySemHandle('\SEM32\mysem') if semh \= '' then do /* something with the handle */ end else do /* it wasn't already there! */ end
SemECreate
semhandle = SemECreate("\sem32\event\semaphore.sem") sem2 = SemECreate("\sem32\event\other.sem", "POSTONE", "AUTORESET");The second example uses both of the optional arguments. These are undocumented, but if you grab a copy of
os2undoc.zip
,
it has a section on Event semaphore extentions. These won't do any good, as
it is documented there, before fixpak 29 on Warp 3, or fixpak 4 for Warp 4.
POSTONE
AUTORESET
AUTORESET
will reset the semaphore before
thread 1 really gets to execute.
Note that if the semaphore is already "created" (for example, by another process), this will simply open it and return the handle for that. Also, even though the system will accept /sem32/blah/blah, this rexx function will not. This may be addressed in future versions. The system does not see a difference, however, between /sem32/blah/blah and \sem32\blah\blah. Since translating "/" to "\" is such an easy thing to do in REXX, it's pretty low priority to change.
Also note that mutex and event semaphores cannot share the same name.
SemEClose
call SemEClose semhandle
SemECloseAll
call SemECloseAllThis will force all event semaphores created in this process to be closed. No semaphore handle is valid after this. Semaphores created by other processes using this DLL are still valid.
SemEPost
call SemEPost semhandleAny processes/threads waiting on this event will be woken up and executed at the next point that OS/2 feels it wants to wake them up (i.e., their next normal time slice).
SemEReset
/* server code. */ do forever call SemEWait semhandle do while queued() parse pull line processLine(line) end call SemEReset semhandle endNote that the reset could happen immediately after the wait, it really doesn't matter - so long as it happens before the next wait.
SemEWait
rc = SemEWait(semhandle, 150) rc = SemEWait(semhandle) rc = SemEWait(semhandle, ,'Waiting for \sem32\somesemaphore.sem')The optional second parameter is a timeout in milliseconds that if the semaphore is not posted within, the failed return value ("TIMEOUT") will be returned. If this is not given, this means indefinite (wait forever).
The optional third parameter is a message to display. The message will be displayed (with a newline) to the user of your rexx script (on stdout) to alert the user that the semaphore is being waited on if it has not already been posted. It will then proceed to wait for the timeout. If the semaphore has been posted by another thread or process, the message will not be displayed.
If the timeout passes without the semaphore being posted, rc will be "TIMEOUT", otherwise it will be "CLAIMED".
SemEQueryPostCount
if SemEQueryPostCount(semhandle) == 0 then do say 'Not posted yet.' endThis will not reset the post count.
SemELogState
call SemELogState 'my.log.file'The my.log.file will be the file to log all the information to. The most important thing would be to ensure that you are using SemEClose on all available semaphores before your script ends. This isn't as severe as mutex semaphores, but it still shows a leak of resources if you're forgetting to close the event semaphores. The logfile will tell you which semhandles have not been closed for this purpose.
If all semhandles are closed, the logfile will not be created.
SemEGetAllSems
call SemEGetAllSems 'sems.' do i = 1 to sems.0 say 'EventSem #'i 'is' sems.i call SemEClose sems.i say 'Woops, make that *was*' sems.i'. It''s closed now.' endNote that if you do not include the '.' at the end of the name, it will be very difficult to loop through them. RXSEM will not put the dot on the end for you.
There is one, somewhat minor, exception to the no dot issue. A subarray is created with the name that was given to each semaphore. For example, if you created the semaphore with "\SEM32\mysem", and this is the first semaphore you created, the following code:
drop name mysem = SemECreate '\SEM32\mysem' call SemEGetAllSems 'sems.' do i = 1 to sems.0 say 'EventSem #'i 'is opened as' sems.i.name endwould have an output similar to:
EventSem #1 is opened as \SEM32\mysem
SemEQuerySemHandle
This is likely to be a "slow" operation. Everything is internally stored in hash tables, but indexed on handle, not name.
Sample usage:
semh = SemEQuerySemHandle('\SEM32\mysem') if semh \= '' then do /* something with the handle */ end else do /* it wasn't already there! */ end
RxSemListWindows
drop
ping the
variable stem you pass in.
call RxSemListWindows 'stem', 'V' /* default - get list of visible-only windows */ call RxSemListWindows 'stem', 'I' /* get list of invisible windows */ call RxSemListWindows 'stem', 'B' /* get list of both visible and invisible windows (ALL) */This function will fill stem in the standard way (stem.0 will have the number of items in the "array"). Extra data will be stored as below, where
stem
represents the stem you passed in, and n
represents the number of the
entry:
stem.n
is the window title
stem.n.pid
shows the process ID of the window.
stem.n.hwnd
shows the handle to the window.
stem.n.session
shows the session ID of the window.
stem.n.visible
is 1 if the window is visible in the window list, 0 otherwise.
stem.n.hswitch
shows the handle to the switch-list entry of the window.
stem.n.progtype
shows the program type of the window which will be one of:
DEFAULT
FULLSCREEN
WINDOWABLEVIO
PM
VDM
WINDOWEDVDM
RxSemToggleSwitchVisibility
Each parameter may be one of the following:
hswitch
given by RxSemListWindows.
The logic used says that it tries the stem first (by putting a '0' after the parameter), then hswitch (by looking to see if there is an hswitch with that number, if the parameter is purely numerical), then partial name.
The return value will be the number of entries successfully toggled, if any. Note that only hswitch's will toggle exactly one entry, partial names may match multiple entries.
Sample code:
call RxSemListWindows 'stem', 'I' /* get list of invisible windows */ do i = 1 to stem.0 /* make each one visible */ if 0 = RxSemToggleSwitchVisibility(stem.i.hswitch) then say "Can't make" stem.i "visible" else say "Made" stem.i "visible" endor
stem.0 = 1 stem.1 = 'Netscape' call RxSemToggleSwitchVisibility 'stem.' /* note the ending .! */
OpenNetscapeWindow
call OpenNetscapeWindow 'http://www.yahoo.com' /* old window */ call OpenNetscapeWindow 'http://www.yahoo.com', 'OLD' /* old window */ call OpenNetscapeWindow 'http://www.yahoo.com', 'NEW' /* new window */
ProcessIDsOf
(Registered)
call ProcessIDsOf 'CMD', 'pids.'Note that the process name may not include a pathname nor the extention (implementation: the scan looks for the process' name ignoring the path and extention, so providing an extention here will find nothing.)
Further, you can cascade the searches with process/stem pairs. For example:
call ProcessIDsOf 'CMD', 'cmds.', 'NETSCAPE', 'netscapes.'This can continue on practically forever. Note, however, that each stem given this way must have different names.
WordWrap
(Registered)
rc = SemMCreateMutex('\SEM32\MY\SEM.SEM') if left(rc, 6) = 'ERROR:' then say WordWrap(rc) else semhandle = rcNot recommended for use in DBCS languages. If you don't know if your language is DBCS, that probably means it isn't. (The only languages I know of that are DBCS are Japanese, Korean, and Traditional/Simplified Chinese.)
RxWinSetTitle
(Registered)
call RxWinSetTitle 'Look, ma, new title!'
PriorityQuery
(Registered)
parse value PriorityQuery() with pri rank select when pri == 'I' then say "Idle priority" when pri == 'R' then say "Regular priority" when pri == 'S' then say "Foreground Server priority!!!" when pri == 'T' then say "Time Critical priority!!!!" otherwise say "Unknown priority of" pri"?" end say "Rank within this class:" substr(pri, 2)As you can tell, the string returned is of form c dd where c is one of I, R, S, or T, and dd is a number between 00 and 31, inclusive.
PrioritySet
(Registered)
DosSetPriority
Control Program API, but are in a somewhat
different order in attempt to allow for more reasonable defaults. Note that
since most REXX scripts are single-threaded, the defaults can be left as is.
(In fact, even in multi-threaded applications, the defaults would usually be
exactly what you wanted anyway.)The function looks like:
PrioritySet(delta, class, scope, processIdentifier)where:
delta
class
scope
processIdentifier
scope
is either P or R,
this is the process identifier. If the scope
is
T, this is the thread identifier. Default is 0, which is
the current process or thread, as appropriate.
Quick lesson: Anything that is I/O-bound (say, a disk cache, listening to the network, etc.) should be at high priority. Anything that is CPU-bound (i.e., computationally intensive) should be put at low priority. Anything at high-priority should do as little as possible when servicing the I/O device, while it is a given that CPU-bound functions are long-time processes. Anything that blocks often can be a candidate to be called I/O. For example, something that is often waiting on an event semaphore, but does very little with the event, could be higher priority to ensure that when the event is posted, it reacts quickly. However, since it spends most of its time completely blocked, it doesn't impact the general performance of the system.
Sample usage:
parse value PriorityQuery() with pri rank call PrioritySet -31, 'I' /* set myself to lowest-possible priority */ 'foo.exe' /* foo will run at the lowest-possible priority unless it * changes its own priority. */ call PrioritySet rank, pri /* reset my priority */
KillProcess
(Registered)
call ProcessIDsOf 'NETSCAPE', 'netscapes.' call KillProcess 'netscapes.'Good way to get rid of all the copies of netscape currently running...
'detach foo.exe | rxqueue' parse pull pidFoo /* some time later ... */ call Killprocess pidFoo /* done with it now */Another example is reading Apache's "httpd.pid" file and using that to kill Apache. There are endless ways to commit murder here, and it's all under your control! BWAhahahahaha ... er... nevermind. You must promise to never use this power for evil, nor to program late at night (like I'm doing).
MyProcessID
(Registered)
SysTempFileName
is that with
this method, you can write a cleanup program that can kill wayward programs
which may have left their files behind, first KillProcess
ing them,
then deleting their file. Sample:
mypid = MyProcessID() call KillProcess mypid /* suicide is illegal in some states/countries, don't try this at home */
SetTextScreenSize
(Registered)
call SetTextScreenSize col, row /* much like mode col,row ! */This function complements the SysTextScreenSize function in REXXUTIL.
ChecksumString
(Registered)
chksum = 0 do while lines(file) > 0 chksum = ChecksumString(linein(file), chksum, 16) end say 'The 16-bit checksum of' file 'is' chksumThis function supports 1 to 32 bit checksums. However, if you want to use 32-bit checksums, be warned that by default, REXX only uses 9-character precision, and 9 decimal digits cannot hold 32-bit precision. You can increase your precision to 10 digits by using
NUMERIC DIGITS 10
. Then again, you only
actually need to do this if you want to do further calcuations after creating
a 32-bit checksum. If you only print it/save it/treat it as a string, then
you don't need to change your NUMERIC settings as doing so can slow down the
rest of your REXX script that does anything numerically.
ChecksumFile
(Registered)
ChecksumString
function, will create
a checksum of a string, this time one stored in a file. Rather than passing in
a string, you pass in the filename. One extra parameter at the end is a flag
saying text or binary when reading the file. This defaults to
binary.
say 'The 20-bit checksum of' file 'is' ChecksumFile(file, 0, 20, 't')All comments for
ChecksumString
apply
to ChecksumFile
as well. This function only provides convenience
and a bit of speed over the ChecksumString
function when dealing with
files. ChecksumString
is merely a general version of this function.