* 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