Pliant forum message

Concerning release: 31
Subject: Question
Title: Handling Signals from external C libraries
initial message posted by John Eikenberry on 2000/02/21 11:05:28
How do I handle Signal? More specificially how do I handle signals raised 
from within a C external library. In my readline wrapper, if I hit Ctr-C while
in the readline wrapped command line interface it raises an error. For instance,
in this simple example:

----
module "/pliant/local/readline.pli"

gvar Str line := rl_get "? "
console line eol
----

If I hit Ctr-C while in the line editing mode, I get an error; eg:

# pliant2 rl_test.pli 
(the '? ' is the prompt from the example, I then hit Ctr-C and get the 'exception 2 ...')
? exception 2
----------------------------------------------------------------
processor stack content is:
???
???
???
???
c_readline (Address) +785
c_readline (Address) +1125
c_readline (Address) +1173
c_readline (Address) +100
readline  /pliant/local/readline.pli (internals) 16 1 /pliant/local/readline.pli (internals) 20 10
rl_get  /pliant/local/readline.pli (internals) 40 1 /pliant/local/readline.pli (internals) 42 11
_noname_  file:t2.pli (internals) 3 1 file:t2.pli (internals) 3 18
pliant internal execute function (Function) +10
. execute (Expression) +345
  file:t2.pli (internals) 3 1
. execute (ParserContext) +40
parser_filter_execute +322
pliant internal parse_one_token function (ParserContext) +468
compile_text (List Module) +245
pliant_load_module (Str Module Int Module) +940
pliant internal startup function +2578
???
----------------------------------------------------------------

After looking into the various modules, I see signal definitions and a few 
functions in the linux.pli and posix.pli modules (I'm using linux). I'm having 
a hard time figuring out how to use these. Could someone give me a brief 
overview?

Thanks.
answer posted by John Eikenberry on 2000/02/21 14:38:37
Progress report:

After messing around with the functions and types in linux.pli, I've been able
to get rid of the previously noted 'exception 2'. It now gives no error, but
simply exits. Here's the code I added to get this behaviour:

gvar os_sigaction test
os_sigaction 2 test test

The os_sigaction contains the Address field sa_handler which, as defined in the
SIGACTION(2) man page, is supposed to contain the action to be taken when the 
signal is raised. As described in the man page, this should be; SIG_DFL, 
SIG_IGN or a pointer to the 'signal handling function'. SIG_DFL isn't defined 
in the linux.pli, but SIG_IGN is defined (os_SIG_IGN). Though I'm not having 
much luck assigning the sa_handle to os_SIG_IGN or to a function. Eg.

test:sa_handler := addressof:os_SIG_IGN

I'll work on it more tommorrow (its bedtime)... mabey I'll get lucky. ;)
answer posted by Hubert Tonneau on 2000/02/21 15:12:20
The main Pliant OS exceptions handler is set in
/pliant/language/debug/report.pli

The first bad news for you is that setting an exception handler for
Linux is not the same as for Posix, which is not the same as for Win32.
The second trouble is that the Pliant execption handler does not provide
any level of flexibility: it displays the error message, then scan the
stack to display the call sequence, then exists.

This is probably a very bad implementation, but I don't know exactly what
a smart behaviour would be (open question), and if I should (my answer is
probably not) provide a standard interface for setting OS exceptions handlers
at application level.
answer posted by John Eikenberry on 2000/02/22 01:45:33
> The first bad news for you is that setting an exception handler for
> Linux is not the same as for Posix, which is not the same as for Win32.

Just a comment here. The Linux specific signal handling has been depreciated 
in favor of posix style. So I wouldn't worry about the Linux specific signals.
Though you might have been speaking about exception handling for more than
signals.

> This is probably a very bad implementation, but I don't know exactly what
> a smart behaviour would be (open question), and if I should (my answer is
> probably not) provide a standard interface for setting OS exceptions handlers
> at application level.

IMO, application level signal handling is a must. Here are a few examples of
applications that use signals (in the hope of showing their usefullness):

1. Unix Daemons: Nearly all daemons use SIGHUP as a signal to re-read their
	configuration files and re-initialize themselves based on any changes. 
	This is a very nice pseudo-standard that would not be possible without
	application level signal handling.

2. RealTimeBattles: This is a game where you write up the brain of a robot.
	It uses signals (SIGHUP again) to let the robot know when there is new 
	information to read (new sensor input).

3. mpg123: The command line mp3 player. This program uses SIGINT as the signal
	to advance to the next song on the playlist.

In regard as to how you should implement signal handling... I'm not sure how
much help I can be in this as I have no experience in how non-posix systems 
(like Win98) implements them... or even if they even have them at all. The best
I can come up with is to create an arbitrary standard (like posix or BSD style)
and try to map the various platforms to it. You could do this in a layered 
approach, with platform specific modules and a platform independent module.
That way you could use the platform independant interface for most circumstances
and the platform specific ones for the rare cases where it causes problems. 

Basically, the only thing I think is really necessary is to have a reasonable
default for a subset of signals (like you seem to have now for kill and suspend)
with a common way to override these with custom functions. 
answer posted by Hubert Tonneau on 2000/02/22 13:26:29
> IMO, application level signal handling is a must. Here are a few examples of
> applications that use signals (in the hope of showing their usefullness):

They are really poor design. A much nicer way to do it would be to open
a named pipe, then read it, so the thread gets locked until a client opens
the named pipe and writes to it, which is the same as raising a signal,
but it's much more powerfull because you can have as many signals as you want,
and send as many informations as required.
The 'signals' mecanism is again the result of the not multithreaded initial
'light' implementation of Unix.

> Basically, the only thing I think is really necessary is to have a reasonable
> default for a subset of signals (like you seem to have now for kill and suspend)
> with a common way to override these with custom functions.

Please, have a look at /pliant/language/stream/tcp.pli
You will find the following sample sequence:

if os_name="linux" or os_name="posix"
  gvar os_sigaction sa
  sa sa_handler := cast os_SIG_IGN Address
  function record_exception_handler parameter filehandle
    arg Address parameter ; arg Int filehandle
    if (os_sigaction os_SIGPIPE sa (null map os_sigaction))<>0
      error error_id_os "Failed to install Linux pliant_signal exception handler"
  record_exception_handler null 0
  gvar DelayedAction da
  da function :> the_function record_exception_handler Address Int
  pliant_restore_actions insert addressof:da

The beginning of it should be clear because it looks very much like C code,
and the four last lines are intended to set the signal handler, and also
record the signal handler setting function so that it be run again each
time the application is lauched. In other words, the 'pliant_restore_actions'
list is intended to redo some initialisation at operating system level when
you load the module through a .dump file instead of recompiling it from
source code.

answer posted by John Eikenberry on 2000/02/23 01:24:01
> Please, have a look at /pliant/language/stream/tcp.pli
<snip>

Thanks for pointing out that code... its a perfect fit for what I needed to do. 

Could you give a brief description of the DelayedAction type used here. It is
currently not documented. And while I think I get the general idea, a more
precise explanation would be useful.

Thanks again.
answer posted by John Eikenberry on 2000/02/23 06:02:07
Ok, I spoke a bit to soon...

To assign a function to sa_handler I need to assign the address of the 
function to it... but I can't seem to figure out how to do this. The
obvious way doesn't seem to work:

function test
 console "this is a test" eol

gvar os_sigaction sa null_sa
sa:sa_handler := addressof:test
os_sigaction 2 sa null_sa

I'm not sure what purpose the second sa (null_sa above) is for... it gets 
assigned to sa_restorer which isn't used in linux anymore, the only reason 
it exists in the C lib is for backwards compatibility.

Anyways, os_sigaction 2 is SIGINT... but when a SIGINT is sent, the program
segfaults. The exception raised and the stack shown is the same as if the
sa_handler had never been reassigned. What is the correct way of assigning
a function to sa_handler so it gets called instead of the default? I think the
more general question of 'how to get the address of a function' would address
this specific issue.

This bring up a larger issue for me. Why are pointers necessary? Since 
everything is an object, shouldn't they all be first class entities and thus 
be passed around with ease (ie. object refernece or implicit pointers). I guess 
I'm just wondering why pointers seem to play such an important role in Pliant. 
Languages like OCaml have shown that they are not needed for performance 
reasons. I can see their usefullness in memory mangagement behind the scenes, 
but couldn't that all be transparent to the applications programmer. 

Just curious. Thanks.
answer posted by Hubert Tonneau on 2000/02/23 14:00:50
A DelayedAction is a way to pass a 'job to do' to a function (a good
example of such a the function is the create a new thread function).
In C, fonctions that receive a 'job to do' have a prototype such as:

void foo(void (*fun)(void *),void *parameter);

The 'DelayedAction' type as a clear advantage over C, which is that
the paramter is an 'Arrow' so when it's not used any more, breaking the
link is enough to get it freed properly, whereas in C, you would need
to provide an extra 'how to free' function.

> function test
>   console "this is a test" eol
> 
> gvar os_sigaction sa null_sa
> sa:sa_handler := addressof:test

change it with:

> function test i
>   arg Int i
>   console "this is a test " i eol
> 
> gvar os_sigaction sa null_sa
> sa:sa_handler := addressof (the_function test Int)

(I have changed your code so that the following explaination be more
 usable)

With Pliant, when you want to execute test, that has no arguments, you
simply write
  test
not
  test()
like with C, so there need to be a different notation for specifying that
you want a pointer to the function instead of executing it, and it is
  the_function test Int
not
  test
like with C.

> I'm not sure what purpose the second sa (null_sa above) is for... it gets 
> assigned to sa_restorer which isn't used in linux anymore, the only reason 
> it exists in the C lib is for backwards compatibility.

With Linux, I call directly the kernel functions, not glibc ones, so if there
are two arguments, it's simply because the Linux kernel function prototype
is so in the man pages, even if the second one may well be ignored.

> This bring up a larger issue for me. Why are pointers necessary? Since 
> everything is an object, shouldn't they all be first class entities and thus 
> be passed around with ease (ie. object refernece or implicit pointers). I guess 
> I'm just wondering why pointers seem to play such an important role in Pliant. 
> Languages like OCaml have shown that they are not needed for performance 
> reasons. I can see their usefullness in memory mangagement behind the scenes, 
> but couldn't that all be transparent to the applications programmer.

First, not everything at all is an object.
An 'Int' can be an object, or not.
Let's take an example:

  gvar Link:Int i :> new Int
'i' is an object.

  function foo
    var Int i
'i' is not an object.

The implementation difference is that in the first case, 'i' is consuming
4 (memory allocation) + 4 (pointer to it's type) + 4 (references count)
+ 4 (value) = 16 bytes
And in the second case, 'i' is consuming only 4 bytes.

As I told to Pactrice on the phone: one key advantage of Pliant over
C++ is that genericity is not related to types, but rather instances.
Any data type is potencialy generical, and it will depend on how the
instance has beeen created. It is a clear advantage because you can
add generical method to existing data types, so you don't need the
data type prototype to provide all methods prototypes.

Back to your question: you're dealing here with low level programming.
If you use 'thread' or 'parallel' instructions, you will have 'DelayedAction'
involved, but will not deal with pointers because Pliant will do it for you,
exactly as you expect.
Keep in mind that none is better:
- low level programming (full of pointers) tend to be trickyer to use, but
  requires less libraries and tend to be more general.
- high level programming is nicer to read, but generaly less general.

As a result, with Pliant, you can deal with both. I do provide a set of
high level functions for general tasks, and plan to provide even more in
the futur, but I also tryed to make the low level features as clean and
usable as possible so that when you really need something, you can do
it yourself.

Again ... again: there is no miracle: if a system is both high level and
efficient, then it is not general, because if it was also general it
would be a large set of researchers, not a computer.
So, Pliant plans to be high level and efficient in some areas, an low
level in all others so that you never have to switch to another language
(which is much worse) in order to get the job done.
answer posted by John Eikenberry on 2000/02/23 15:16:44
> function test i
>   arg Int i
>   console "this is a test " i eol
> 
> gvar os_sigaction sa null_sa
> sa:sa_handler := addressof (the_function test Int)
> os_sigaction 2 sa null_sa

Well, I hate to say it, but this doesn't seem to make any difference. When
I try this, and raise the SIGINT pliant segfaults (run at debug0) or spits out 
an 'exception 11' and giving a stack trace just like in my first post above 
(run at debug2). So it doesn't seem to be running it as it should be... if it
should work, mabey this is a bug. Just so you know it does work in some situations,
the following code works fine. If I raise a SIGINT with this code in place, 
nothing happens (as it should).

 gvar os_sigaction sa null_sa
 sa:sa_handler := cast os_SIG_IGN Address
 os_sigaction 2 sa null_sa

In releation to why you added the Int argument to test above. Was that necessary
for the the_function to work correctly? Wouldn't '(the_function test)' work
the same for my original version? It gives the same error as the above code does,
which would seem to suggest so.

Oh... and thanks for the good explanation of my pointers question. It was very
helpful. 
answer posted by Hubert Tonneau on 2000/02/24 00:11:43
John Eikenberry wrote:
> 
> Here's the shorttest one I could come up with that was self contained. Just
> run this code at debug2 and you'll see what I'm talking about when you hit
> a Ctr-C.

There was no problem with Pliant: I put the correct program on the forum so
that the code may be found at a later time by another user with the same needs
(must be running Linux).

module "/pliant/language/unsafe.pli"
module "/pliant/language/compiler.pli"
module "/pliant/language/os.pli"

function test
  external_calling_convention
  console "this is a test" eol

entry_root addressof:(the_function test)
gvar os_sigaction sa
sa sa_handler := (the_function test) executable
os_sigaction os_SIGINT sa (null map os_sigaction)

function readline prompt -> line
  arg CStr prompt line
  external "libreadline.so.2" "readline"

gvar Str line := readline "? "
console line eol

Explainations:
- you need 'external_calling_convention' because the OS will call
  your function using general OS calling convention, not compiler one.
- in the 'sa_handler' field, you need to set the address of the
  function code.
    the_function test
  is the address of the function as a Pliant object
    (the_function test) executable
  is the address of the executable part (where the processor must
  jump to in order to have the function executed)
- 'entry_root' is recommended because so that the function be
  locked in memory. It may be necessary to lock the function
  in memory because the OS sa_handler does not know with Pliant
  reference count mecanism.
- I had to specify "libreadline.so.2" because there is no
  "libreadline.so" with Debian potato.
answer posted by John Eikenberry on 2000/02/24 00:58:25
I guess the fact that 'sa:sa_handler := cast os_SIG_IGN Address' worked 
fine made me think it would work the same for the other cases. I didn't
realize that the usage of an external library would effect this or I would
have included that in the earlier message. Thanks for the clarification. 
BTW, I just tried it and it worked like a charm. 

> - I had to specify "libreadline.so.2" because there is no
>   "libreadline.so" with Debian potato.

It's in the libreadline4-dev package (in potato).

Thanks again for all the help.