[comp.os.os2] signals, threads, and memory allocation

jamin@eddie.mit.edu (Sugih Jamin) (11/10/89)

In article <8551@microsoft.UUCP> robertre@microsoft.UUCP (Robert Reichel ms2) writes:
>In article <32295@ucbvax.BERKELEY.EDU> jamin@cogsci.berkeley.edu (Sugih Jamin) writes:
>> [Why does one need to reinstate a signal handler after every use?]
>To control reentrancy in your signal handler.

This point has been well discussed by others.  I just want to mention that
it would have been nice of OS/2 to take care of it the way BSD does, instead
of having the programmer take care of it {him,her}self.

>>One doesn't have to do the same in BSD, does one?  
>Don't know.  Does it matter?

I think it was Newton who said something about standing on the shoulders of
giants.  I mentioned BSD just to point out a different implementation which
I happen to think is cleaner.  Similarly, I think Mach's way of handling 
threads is much more elegant than OS/2's.

>>Some time ago I posted a message about managing stacks for
>>threads.  Does anybody, especially people at Microsoft, know 
>>why OS/2 was not designed to manage the stacks internally?
>
>Consider that different applications may have different needs.
>It is reasonable for an application that is going to be creating
>and killing lots of threads to maintain a pool of stacks for those
>threads, so as not to endure the overhead of continually having
>to allocate and free stack space (which may fail in tight memory
>situations).  The idea was to provide flexibility to the application
>writer.

The overhead shouldn't be too bad if OS/2 manages threads' carcasses
the way UNIX does those of child processes.  A fork(2) simply breathes
new life into an abandoned cadaver, if I'm not wrong.

>>Assume that the total amount of memory I need at 
>>any one time is not more than 64K (otherwise I could use DosAllocHuge,
>>which brings me to my next question: will DosSubAlloc/DosSubFree
>>perform slower on a huge chunk of memory?)  
>
>You cannot use the suballocation calls on a huge segment.  Read
>the manual entry for DosSubSet.

Ah, the manual.  It, the manual, says that I must use DosSubSet on a 
chunk of memory allocated with DosAllocSeg or DosAllocShrSeg.  It 
doesn't say I must not use it with DosAllocHuge.  Normally, I would 
assume that only DosAllocSeg and DosAllocShrSeg work with DosSubSet.  
But I have been burned once for assuming what the manual doesn't say.  
I am referring to DosGiveSeg and DosGetSeg.  The manual for DosGetSeg 
says that even when one already has a selector to the shared memory, 
one still has to call DosGetSeg.  But it turns out that one doesn't need to 
call DosGetSeg if the selector one got came from DosGiveSeg.  I found
this out from the M&T book.  Besides, calling DosSubSet on memory
allocated with DosAllocHuge didn't return any error.

Now that we have mentioned the manual, allow me to ask, "Why is
Microsoft so inconsistent in making up function names?"  For dealing 
with semaphores, for example, we have DosOpenSem, DosCreateSem, etc.,
but we also have DosSemRequest, and DosSemWait.  A simple test:
is it DosSemClear or is it DosClearSem one should call to clear a
semaphore?  Then, what is the difference between DosGet and DosQuery?
Is it DosGetMachineMode or DosQueryMachineMode?  

Then there is that Hungarian notation.  It simply defeats the whole
purpose of data abstraction, doesn't it?  Even the manual itself got
confused over it.  In more than one instances, the manual errorneously
describes a variable which name begins with a 'p' as a pointer.  Not
to mention that the manual for LAN Manager doesn't follow the same
notation (nor does it use the OS/2 types).

Somebody here said that OS/2 has a lot of neat features, just that
some of them are not properly implemented.  I totally agree with
this view.  Even the manual is not properly prepared.  OS/2 in its 
current state really is half of an operating system.


sugih

ander@pawl.rpi.edu (Michael R. Primm) (11/11/89)

One problem with emulating the fork(2) idea of "resurrecting" old thread
cadavers is that threads should never have cadavers in the first place.
The whole distinction between a thread and a child process is that a thread
shares EVERYTHING with its fellow threads  Therefore,  OS/2 processes
don't spin off copies of themselves (and their data areas) when a new thread
is created, only a new context is created.
In any case, I do like having the option of controlling the threads at a 
low level (just handing the thread a stack and starting it has got to be
faster than having the operating system allocate the space, start the thread,
and remember to deallocate the space after the thread dies).  BUT, I think 
such an option would have been very helpful, given the likely frequent use of
such a feature.
                                                    --Mike Primm

guy@auspex.auspex.com (Guy Harris) (11/11/89)

>"Does it matter" means "is it important what BSD does since we're talking
>about OS/2".  Of course the behavior must be well defined for each
>operating system.

The behavior was "well-defined" in pre-BSD UNIX, it just wasn't very
*good*.  "Well-defined" isn't sufficient, and, therefore, the fact that
it does matter under UNIX leaks into OS/2 as well, if OS/2's behavior
loses as pre-BSD UNIX's does....

>>This meant that the signal handler wouldn't be reentered
>>if another instance of the same signal were delivered to the process
>>while handling one instance of that signal, unless the process
>>re-established the signal handler; instead, the process would simply
>>die!  This has been known to kill real live applications.
>
>This is, I believe, one of the reasons signal() was replaced by
>sigset()?

Yup, although "sigset" was replaced by "sigvec" in 4.2BSD, which was
adopted in modified form in POSIX as "sigaction".

>In OS/2 the return does not implicitly acknowledge the signal.
>One must call DosSetSigHandler to acknowledge the signal.  I'm
>no BSD expert, but from what you describe it sounds as if the
>implicit blocking of further signals upon entering the signal
>handler is similar to OS/2's behavior, ie, they are latched in
>one at a time at well defined places.

Well, I'm no OS/2 expert, I just know what I found in the aforementioned
book, so I can't compare them, either.  Here's the BSD behavior,
hopefully described without need for implicit knowledge of said
behavior; how does OS/2 behave (as described without need for implicit
knowledge of said behavior - i.e., I don't know what "latched in" means
or what the "well defined places" are, or what it means to "acknowledge"
the signal, so it's hard for me to say whether that matches BSD behavior
or not):

A process has a "signal mask", with one bit for each possible signal
(e.g. since SIGINT - which seems to correspond to the OS/2 "CTRL-C"
signal, value 1 - has value 3, the third bit corresponds to SIGINT:

	/*
	 * Macro for converting signal number to a mask suitable for sigblock().
	 */
	#define sigmask(m)	(1 << ((m)-1))

from 4.3BSD).  If the bit for a given signal is not set, the signal is
not blocked, which means that if it is sent to a process (either with a
"kill()" call or by the system internally generating the signal), the
action specified for the signal is taken.  This action can be:

	SIG_IGN, which is "ignore", i.e. completely throw the signal
	   away, don't queue it;

	SIG_DFL, which is "terminate process" except for some signals
	    where it's "ignore";

	or call the handler specified for that signal.

If the bit for a given signal *is* set, the signal is blocked, which
means that if it is sent to a process, the action is *not* taken, but a
bit is set in an internal mask specifying that the signal is pending. 
When the process's signal mask is changed so that the bit gets turned
off, i.e. the signal is unblocked, the action specified for the signal
is taken at that time.

If the action for a non-blocked signal is to call a handler, when the
handler is called the signal is then blocked, so that further
occurrences of the signal are posted (actually, only one is posted; if
the signal is sent 5 times while it's blocked, it will be delivered only
once when unblocked) but not taken.  If the signal handler returns
normally (e.g., doesn't use "longjmp"), the signal is automatically
unblocked.

A process can also explicitly block a signal with "sigblock()", or set
the signal mask to an arbitrary value, and get the previous value, with
"sigsetmask".

The "OS/2 Programmer's Guide" doesn't make any explicit mention of a
signal mask, so I don't know whether, when a signal handler is called:

	the signal is blocked, so that further occurrences are posted
	  but not delivered;

	the signal action becomes "terminate process", so that
	  further occurrences kill the process, as in the bad old days
	  of UNIX;

	the signal action becomes "ignore", so that further occurrences
	  are not only not delivered, but not even posted;

	none of the above.

If the signal is blocked, does DosSetSigHandler unblock it?

ander@pawl.rpi.edu (Michael R. Primm) (11/12/89)

According to "Advanced OS/2 Programming" by Microsoft Press, when a signal
handler is invoked, no further signals of that type are processed until
DosSetSigHandler() is called again with an action code of 4 (reset current
signal).
   Also, DosHoldSignal() can be used to temporarily postpose signal
handling (signals generated while a hold is in effect are not lost, but
wait until the hold ends).
   As far as I can tell, "masking" signals can be done by simply setting
them to have a handler which does nothing but reset the current signal (
or possibly nothing, but I'm not sure if some additional burden would be
put on the system by accumulating signals (if they do accumulate...I tend
to doubt it, since OS/2's signals are meant to emulate hardware interrupts)).
If you wanted to, writing up a routine which could enable and disable 
signals using a mask should be easy....just keep a static array of pointers
to each signal handler, define a "null" handler routine, and have the routine
remake a DosSetSigHandler call for each signal which changes state (enabled to
disabled, or vice versa).
   Basically, OS/2's kernal support is complete enough to generate pretty much 
any service you want....since most of the calls are supported as DLLs, anyone
can write up additional services which can work just as well.  IMHO, its
kinda nice having access to lower level calls...you don't have a specific
philosophy imposed upon you as much as on other systems (i.e. you can have
any semaphores you want, as long as they're FIFO, etc).
                                                       --Mike Primm

robertre@microsoft.UUCP (Robert Reichel ms2) (11/14/89)

In article <1989Nov10.025654.3274@eddie.mit.edu> jamin@eddie.MIT.EDU (Sugih Jamin) writes:
>In article <8551@microsoft.UUCP> robertre@microsoft.UUCP (Robert Reichel ms2) writes:
>>In article <32295@ucbvax.BERKELEY.EDU> jamin@cogsci.berkeley.edu (Sugih Jamin) writes:
>>> [Why does one need to reinstate a signal handler after every use?]
>>To control reentrancy in your signal handler.
>
>This point has been well discussed by others.  I just want to mention that
>it would have been nice of OS/2 to take care of it the way BSD does, instead
>of having the programmer take care of it {him,her}self.

Fine.  I disagree.

>Ah, the manual.  It, the manual, says that I must use DosSubSet on a 
>chunk of memory allocated with DosAllocSeg or DosAllocShrSeg.  It 
>doesn't say I must not use it with DosAllocHuge.  Normally, I would 
>assume that only DosAllocSeg and DosAllocShrSeg work with DosSubSet.  
>But I have been burned once for assuming what the manual doesn't say.  
>I am referring to DosGiveSeg and DosGetSeg.  The manual for DosGetSeg 
>says that even when one already has a selector to the shared memory, 
>one still has to call DosGetSeg.  But it turns out that one doesn't need to 
>call DosGetSeg if the selector one got came from DosGiveSeg.  I found
>this out from the M&T book.  

The manual is correct, so I don't see how you were "burned".  DosGiveSeg
makes a selector valid in another process's context (ie copies descriptor
information from the LDT of the process making the GiveSeg call into
the LDT of the target process).  The recipient is able to use the segment
immediately, so of course you don't need to call DosGetSeg.

DosGetSeg is a two step process.  First, you must find out the *value*
of the selector you want to get.  You must do this via shared memory
or your favorite IPC mechanism.  Once you have the value, you must call
DosGetSeg to make it valid in your context (ie copy its descriptor
information into your LDT).  Simply knowing a descriptor value won't
do a darn thing for you.  Similarly, GiveSeg is also a two step process.
First call GiveSeg to implant the descriptor information in the target
process, and *then* pass the descriptor to the target so that it knows
which one to use.  Note that GiveSeg returns the value that is to be
passed to the target.

>Besides, calling DosSubSet on memory allocated with DosAllocHuge 
>didn't return any error.

And it will work correctly in the first 64K subsegment.  If you choose
to say that this means that it will "work" for a huge segment, that's
up to you.  The code will not allocate anything from subsequent 64K
subsegments.
-- 
My opinions are my own and to not necessarily reflect those of Microsoft
Corporation.
Robert Reichel		 {decvax,uunet,uw-beaver}!microsoft!robertre

robertre@microsoft.UUCP (Robert Reichel ms2) (11/14/89)

In article <2620@auspex.auspex.com> guy@auspex.auspex.com (Guy Harris) writes:
>	[description of signal bitmask deleted]
>
>If the bit for a given signal *is* set, the signal is blocked, which
>means that if it is sent to a process, the action is *not* taken, but a
>bit is set in an internal mask specifying that the signal is pending. 
>When the process's signal mask is changed so that the bit gets turned
>off, i.e. the signal is unblocked, the action specified for the signal
>is taken at that time.

What you're describing is the same.  We maintain an extra piece
of information, because it is illegal to send a signal (other than
SIG_KILL) to a process that does not have a handler for it.  So
in addition to maintaining the current disposition of the signal,
we need to keep track of whether it's a valid signal for that process
in the first place.

>If the action for a non-blocked signal is to call a handler, when the
>handler is called the signal is then blocked, so that further
>occurrences of the signal are posted (actually, only one is posted; if
>the signal is sent 5 times while it's blocked, it will be delivered only
>once when unblocked) but not taken.  If the signal handler returns
>normally (e.g., doesn't use "longjmp"), the signal is automatically
>unblocked.

This is identical to OS/2, except for the automatic unblocking on
return.  Doesn't your last sentence contradict what
an earier poster described, ie the "complex implementation that 
acknowledges the signal even if the process does a longjmp" (no,
not an accurate quote, but that's how I understood what he said).
I don't have the article with the statement handy.

One "unblocks" a signal by acknowleging it.

>A process can also explicitly block a signal with "sigblock()", or set
>the signal mask to an arbitrary value, and get the previous value, with
>"sigsetmask".

Roughly the same.  Substitute DosHoldSignal for sigblock() (I'd imagine, since
sigblock() may do things that I'm not aware of).  We don't allow direct
manipulation of the bitmap.

>The "OS/2 Programmer's Guide" doesn't make any explicit mention of a
>signal mask, so I don't know whether, when a signal handler is called:

>	the signal is blocked, so that further occurrences are posted
>	  but not delivered;

Yes, as described above.

>	the signal action becomes "terminate process", so that
>	  further occurrences kill the process, as in the bad old days
>	  of UNIX;

No.

>	the signal action becomes "ignore", so that further occurrences
>	  are not only not delivered, but not even posted;

No.  At most one is posted, subsequent attempts return error to the sender.

>If the signal is blocked, does DosSetSigHandler unblock it?

Yes, when called with SIG_ACK.
-- 
My opinions are my own and to not necessarily reflect those of Microsoft
Corporation.
Robert Reichel		 {decvax,uunet,uw-beaver}!microsoft!robertre

guy@auspex.auspex.com (Guy Harris) (11/17/89)

>What you're describing is the same.  We maintain an extra piece
>of information, because it is illegal to send a signal (other than
>SIG_KILL) to a process that does not have a handler for it.

Hmm.  The *OS/2 Programmer's Guide* says "To receive a flag event, the
process must register a signal handler in Thread 1 with the
DosSetSigHandler function call.  If this registration is not done, the
system handles the flag for the process and takes appropriate default
actions."  That sounds more like the UNIX behavior than what you
describe; i.e., you're allowed to send a signal with DosFlagProcess even
if the process hasn't explicitly registered a handler.  (Which one is
SIG_KILL?  The *OS/2 Programmer's Guide* lists signal 3 as "Terminate";
is that the one to which you're referring?)

>Doesn't your last sentence contradict what an earier poster described,
>ie the "complex implementation that acknowledges the signal even if
>the process does a longjmp" (no, not an accurate quote, but that's how
>I understood what he said).

In vanilla BSD, "longjmp" does restore the signal mask and unblock the
signal.  In POSIX, it does so only if the matching "setjmp" was actually
a "sigsetjmp" that specified that the signal mask be saved; otherwise,
it doesn't.

>One "unblocks" a signal by acknowleging it.

The word "acknowledge" doesn't appear in the index of the *OS/2
Programmer's Guide*, or at least not the edition here, and it definitely
doesn't refer to registering a signal handler with DosSetSigHandler as
"acknowledging" it.  You later say that DosSetSigHandler unblocks a
signal if called with SIG_ACK, but the *OS/2 Programmer's Guide* doesn't
mention any argument that would mean something like that, so either the
Programmer's Guide is incorrect or the interface to DosSetSigHandler
changed after that edition of the Programmer's Guide was written,
apparently.  (The one from which I'm reading this has no "Nth edition"
numbering in any obvious place, and has a copyright date of 1988.)

>Roughly the same.  Substitute DosHoldSignal for sigblock() (I'd imagine, since
>sigblock() may do things that I'm not aware of).

All "sigblock()" does is 1) add the signal in question to the list of
signals being blocked and 2) return the previous signal bit mask.

>We don't allow direct manipulation of the bitmap.

That could be a problem if you implement a POSIX interface to OS/2,
since POSIX *does* allow direct manipulation of the signal mask. 
Presumably, if, as, and when a POSIX interface is provided for OS/2, a
mechanism will be provided to allow direct manipulation of the signal
mask.

ccastmj@prism.gatech.EDU (Joseph Lawrence Martin) (11/18/89)

In message: <1989Nov10.025654.3274@eddie.mit.edu>
jamin@eddie.mit.edu (Sugih Jamin), writes:
> Ah, the manual.  It, the manual, says that I must use DosSubSet on a 
> chunk of memory allocated with DosAllocSeg or DosAllocShrSeg.  It 
> doesn't say I must not use it with DosAllocHuge.  Normally, I would 
> assume that only DosAllocSeg and DosAllocShrSeg work with DosSubSet.  
> ...
> this out from the M&T book.  Besides, calling DosSubSet on memory
> allocated with DosAllocHuge didn't return any error.

All DosAllocHuge does is allocate a series of memory blocks whose
selectors are evenly spaced throughout one's LDT (this space between
them is found from a function call).  Once a block's selector is
known, it can be used as if it were a stand-alone memory segment
when used with "simple" calls like DosSub{Set,Alloc,Free}, but only
as a single segment.  Those functions will not cross the segment
boundaries.  I think that the only connection that remains between
the segments allocated with DosAllocHuge is that they can be deallocated
all at once (I think) with a function call.

> Now that we have mentioned the manual, allow me to ask, "Why is
> Microsoft so inconsistent in making up function names?"  For dealing 
> with semaphores, for example, we have DosOpenSem, DosCreateSem, etc.,
> but we also have DosSemRequest, and DosSemWait.  A simple test:

I think that the reason behind the inconsistent naming "conventions"
is tied to the classic "7-letter limit" for distinct external link-time
references, and if all the semaphore commands were DosSem..., then
there would only be 1 letter left (at least) for distinguishing all the
semaphore commands (risky).  That is, DosSemWait and DosSemWhatsit would
be the same.  I am not sure if this is totally accurate, but it looks
plausible.
  Of course, they could have been named by different committees working
on commands split up arbitrarily, but that never happens. :-)

> Then there is that Hungarian notation.  It simply defeats the whole
> purpose of data abstraction, doesn't it?  Even the manual itself got
> confused over it.  In more than one instances, the manual errorneously

Yes, that notation is weird and ambiguous at best.
And mentioning confused manuals, I have noticed several inconsistencies
in Iacobucci's (no offense) book on OS/2, mainly in some of the
descriptions of the interfaces to the function calls.  I would use
the descriptions in the book, but when I was unsure about a function's
interface, I would have to break out the Tech Ref.  Maybe these glitches
will be fixed in a later edition of the book.

Disclaimer: I speak for myself alone, and not for my employer (as if
any of you know where I work :-).
-- 
Joseph Lawrence Martin
Georgia Institute of Technology, Atlanta Georgia, 30332
uucp:	  ...!{decvax,hplabs,ncar,purdue,rutgers}!gatech!prism!ccastmj
Internet: ccastmj@prism.gatech.edu

robertre@microsoft.UUCP (Robert Reichel ms2) (11/21/89)

In article <2639@auspex.auspex.com> guy@auspex.auspex.com (Guy Harris) writes:


>Hmm.  The *OS/2 Programmer's Guide* says "To receive a flag event, the
>process must register a signal handler in Thread 1 with the
>DosSetSigHandler function call.  If this registration is not done, the
>system handles the flag for the process and takes appropriate default
>actions."  That sounds more like the UNIX behavior than what you
>describe; i.e., you're allowed to send a signal with DosFlagProcess even
>if the process hasn't explicitly registered a handler.  (Which one is
>SIG_KILL?  The *OS/2 Programmer's Guide* lists signal 3 as "Terminate";
>is that the one to which you're referring?)

I don't have a copy of this handy.  I assume it's what came with the
SDK?  Regardless, you can bang on something with DosFlagProcess all
you want, but unless it has a handler registered, you're going to
get an error back.

SIG_KILL is what is sent by DosKillProcess.  You can't send it via
DosSendSignal.

I apologize for the confusion on some of the names and constants, for
reasons unknown to me the same thing is often referred to by different
names in different places.  The IBM Tech ref doesn't use SIG_KILL, but
rather SIGTERM.

>>One "unblocks" a signal by acknowleging it.
>
>The word "acknowledge" doesn't appear in the index of the *OS/2
>Programmer's Guide*, or at least not the edition here, and it definitely
>doesn't refer to registering a signal handler with DosSetSigHandler as
>"acknowledging" it.  You later say that DosSetSigHandler unblocks a
>signal if called with SIG_ACK, but the *OS/2 Programmer's Guide* doesn't
>mention any argument that would mean something like that, so either the
>Programmer's Guide is incorrect or the interface to DosSetSigHandler
>changed after that edition of the Programmer's Guide was written,
>apparently.  (The one from which I'm reading this has no "Nth edition"
>numbering in any obvious place, and has a copyright date of 1988.)

Again, I'm not sure what's in the Programmer's Guide.  The IBM Tech
Ref describes the acknowledge parameter as having a value of 4
and the action of "The current signal is reset without affecting
the disposition of the signal."  Here's the lot:

Value	Description
0	The system default action is installed for the signal
1	The signal is to be ignored
2	The routine receives control when the SigNumber occurs
3	It is an error for any program to signal this SigNumber to this process
4	The current signal is reset without affecting the disposition of the signal

SigNumber
Number	Term		Definition
1	SIGINTR		Ctrl-C
3	SIGTERM		Program terminated
4	SIGBREAK 	Ctrl-Break
5			Process Flag A
6			Process Flag B
7			Process Flag C

Not to sound like I'm plugging a product, but a 4th volume of the
OS/2 Programmer's Reference just came out, and it has a fair number
of corrections to the stuff that was in the first three volumes.
They missed some stuff (sigh) but fixed quite a bit also.  It might
clear up some confusion out there.
-- 
My opinions are my own and to not necessarily reflect those of Microsoft
Corporation.
Robert Reichel		 {decvax,uunet,uw-beaver}!microsoft!robertre

robertre@microsoft.UUCP (Robert Reichel ms2) (11/21/89)

In article <9094@microsoft.UUCP> robertre@microsoft.UUCP (Robert Reichel ms2) writes:
>I don't have a copy of this handy.  I assume it's what came with the
>SDK?  Regardless, you can bang on something with DosFlagProcess all
>you want, but unless it has a handler registered, you're going to
>get an error back.

Ha!  What a hosehead.  The default action for a flag is ignore, so
the sender won't get an error back.  Serves me right for trusting
my memory.

Sorry 'bout that.  Even Mother makes mistakes sometimes...
-- 
My opinions are my own and to not necessarily reflect those of Microsoft
Corporation.
Robert Reichel		 {decvax,uunet,uw-beaver}!microsoft!robertre