comp.lang.ada
 help / color / mirror / Atom feed
From: Adam Beneschan <adam@irvine.com>
Subject: Re: Ada Thread Termination
Date: Mon, 4 Feb 2008 09:57:37 -0800 (PST)
Date: 2008-02-04T09:57:37-08:00	[thread overview]
Message-ID: <48309f0b-8474-4973-aaba-50b5d63f0abd@m34g2000hsb.googlegroups.com> (raw)
In-Reply-To: f7866882-e806-46a6-a97e-a33bdf042338@e10g2000prf.googlegroups.com

On Feb 2, 11:41 am, shaunpatter...@gmail.com wrote:
> I have a socket listening thread that I would like to be able to
> terminate via a select call.
>
> task body Socket_Listener is
>   begin
>      accept Initialize do
>            -- setup
>      end Initialize;
>
>      loop
>            Listen_For_Message;
>      end loop;
>   end Socket_Listener
>
> I would like to have another accept statement inside the loop to break
> out of the loop.
>
> Listen_For_Message is actually a call to a blocking socket read, so
> the call may not finish if nothing else comes through the socket. I'm
> not sure how to structure this thread so it can be terminated on
> demand.

Well, Dmitry's suggestion might work in this particular case.

However, in general, I'm not sure it's a good idea to put blocking
calls like this in a task; they may not work.  Conceptually, when you
start using concurrency and synchronization in Ada (i.e. tasking), the
Ada tasking model should be in control of all aspects of concurrency,
blocking, dispatching, etc.; if you go outside the Ada tasking model,
the tasking support library may not be able to handle it because it
won't know what you're doing.  Some implementations may be better than
others that this, and it will probably depend on what kind of OS
you're running on.

To take a simple example: Although you're referring to a multi-
threaded program, it's possible to implement tasking with a single
thread, possibly using a timer to interrupt the program at various
intervals so the tasking library can perhaps check to see if a higher-
priority task is ready to run.  (I believe this is possible, although
it may be impossible to support Annex D this way.)  Anyway, suppose
that, in this sort of implementation, one of your tasks does a Unix
read() to get input from some external source.  While the read() is in
progress, your program will stop.  The other tasks will not be
running, even if they're able to run, because read() has blocked the
program outside of the knowledge of the tasking library, and the
tasking library will not know what to do.

In an implementation where each task is its own thread, the OS will
run other tasks.  But there may still be other problems, such as the
one you're dealing with.  Things will work fine if your blocking call
will eventually return, but what if you want to force it to
terminate?  The tasking model isn't going to help much since you've
gone outside the tasking model (behind its back, so to speak).

Although you may find a solution that works for a particular
implementation and OS, I'd like to suggest that perhaps your socket-
handling code itself rely on Ada tasking, perhaps by using a task type
to represent a particular socket, so that Listen_For_Message would be
an entry call rather than a procedure call (or, perhaps, a procedure
call that makes two entry calls, one to start the socket operation and
one that waits for it to complete).  If it's an entry call into a
task, the semantics of what happens when an entry call blocks are well-
defined by the language; but the semantics of a blocking procedure
call, and how it interacts with the rest of tasking, aren't defined.

I'm not totally sure how you'd implement the socket task, except that
it would probably involve the interrupt handling features of C.3, or
(if you're running on Windows, say) some other low-level way provided
by the implementation to tie a Windows event object (?) to a (task or
protected) entry call.  (Sorry, I haven't looked up how an
asynchronous socket operation would be called on Windows.)

Anyway, I'd start thinking about those lines if I wanted a somewhat
portable solution to your problem (there will probably be some
implementation-dependent stuff at the low level anyway).  If you don't
care about portability, you probably need to tell us what Ada
implementation and OS you're using.

Having said all that, this *might* work:

    select
        Termination_Task.Terminated;
        Terminated := True;
    then abort
        Listen_For_Message;
    end select;

"select then abort" needs an entry call or a delay statement to tell
it when to abort the abortable part, so you'll need to set up some
sort of entry in a third task, something like this:

    task body Termination_Task is
    begin
       accept Time_To_Terminate;
       accept Terminated;
    end;

Then, when some other task decides it's ready to terminate the
program, it calls the Time_To_Terminate entry; this then causes
Termination_Task to accept the Terminated entry, which causes the
"select" to abort the Listen_For_Message because the triggering
alternative (the entry call to Terminated) has been triggered.  I'm
sure more logic is needed than the above simple code, but hopefully
you get the idea.  The problem is, you have to ask yourself what
happens when Listen_For_Message is aborted?  You don't know,
necessarily.  Would the socket read be aborted, or would the OS still
think it's waiting for the read?  Is some sort of cleanup necessary,
or a "cancel" operation?  Is aborting a thread while it's waiting for
a socket read going to screw things up?  (If this is Windows, the
answer is "probably".  It doesn't seem to take much to screw up a
Windows system.)  What if the abort happens at the exact same time
when the socket read completes, or a few microseconds after?  If you
want a reliable program, all these questions have to be considered,
which is another reason I think it's important to have operations like
this use the tasking model, or be more integrated with it at a lower
level; IMHO it's easier to make sure you get all this correct if you
keep control of the situation by using Ada tasking, rather than just
hoping things work and don't crash the system.  (On the other hand, if
this is a Windows app, crossing your fingers and hoping things don't
crash seems to be the accepted normal way of doing things.)

                                 -- Adam




  parent reply	other threads:[~2008-02-04 17:57 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2008-02-02 19:41 Ada Thread Termination shaunpatterson
2008-02-02 19:55 ` Dmitry A. Kazakov
2008-02-02 21:23 ` gpriv
2008-02-04 17:57 ` Adam Beneschan [this message]
2008-02-04 23:26   ` Robert A Duff
replies disabled

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox