comp.lang.ada
 help / color / mirror / Atom feed
* Ada Thread Termination
@ 2008-02-02 19:41 shaunpatterson
  2008-02-02 19:55 ` Dmitry A. Kazakov
                   ` (2 more replies)
  0 siblings, 3 replies; 5+ messages in thread
From: shaunpatterson @ 2008-02-02 19:41 UTC (permalink / raw)


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.

Any ideas?

Thanks

--
Shaun



^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: Ada Thread Termination
  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
  2 siblings, 0 replies; 5+ messages in thread
From: Dmitry A. Kazakov @ 2008-02-02 19:55 UTC (permalink / raw)


On Sat, 2 Feb 2008 11:41:55 -0800 (PST), shaunpatterson@gmail.com wrote:

> 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.
> 
> Any ideas?

Close the socket from outside, that would cause listening to end with a
"socket closed" error, so the listener task could quit.

-- 
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de



^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: Ada Thread Termination
  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
  2 siblings, 0 replies; 5+ messages in thread
From: gpriv @ 2008-02-02 21:23 UTC (permalink / raw)


On Feb 2, 2:41 pm, 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.
>
> Any ideas?
>
> Thanks
>
> --
> Shaun

In addition to what Dmintry has suggested you may consider setting
receive timeout when opening the socket.

task body socket_listener is
begin
    select
       accept initialize ...
    or
       terminate;
    end select;

    while socket_is_open loop

       select
          accept close_connection do
              gracefully close socket here
          end close_connection
       else
          listen_for_message_ignore_timeouts...
       end select;

    end loop;

end socket_listener;

be aware that there is a bug in windows: socket timeouts are
interpreted in milliseconds

Regards,

George Privalov



^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: Ada Thread Termination
  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
  2008-02-04 23:26   ` Robert A Duff
  2 siblings, 1 reply; 5+ messages in thread
From: Adam Beneschan @ 2008-02-04 17:57 UTC (permalink / raw)


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




^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: Ada Thread Termination
  2008-02-04 17:57 ` Adam Beneschan
@ 2008-02-04 23:26   ` Robert A Duff
  0 siblings, 0 replies; 5+ messages in thread
From: Robert A Duff @ 2008-02-04 23:26 UTC (permalink / raw)


Adam Beneschan <adam@irvine.com> writes:

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

I've used something like Dmitry's suggestion in the past,
and it worked fine.

> 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.

In some implementations, it might block the entire program,
which is not what you want.  But I think in most implementations,
including GNAT, it will block the current task, which is fine.

> 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.)

I don't see the need for the timer interrupt in such an implementation.
And I don't see the problems with respect to Annex D.
I do see a problem with blocking system calls, as you note below:

>...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.

So in such a system, non-blocking I/O, with interrupt on completion,
is needed.

> Having said all that, this *might* work:
>
>     select
>         Termination_Task.Terminated;
>         Terminated := True;
>     then abort
>         Listen_For_Message;
>     end select;

Aborting system calls is treading on thin ice.  The Ada RM doesn't
require it to work, and in many cases it does not work.
So your "might" above should perhaps be a "will probably not".  ;-)

> "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;

A protected object might be simpler than a task, here.

- Bob



^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2008-02-04 23:26 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
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
2008-02-04 23:26   ` Robert A Duff

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