From mboxrd@z Thu Jan 1 00:00:00 1970 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on polar.synack.me X-Spam-Level: X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00 autolearn=ham autolearn_force=no version=3.4.4 X-Google-Thread: 103376,7322e496af76698c X-Google-Attributes: gid103376,public,usenet X-Google-Language: ENGLISH,ASCII-7-bit Path: g2news1.google.com!news2.google.com!postnews.google.com!m34g2000hsb.googlegroups.com!not-for-mail From: Adam Beneschan Newsgroups: comp.lang.ada Subject: Re: Ada Thread Termination Date: Mon, 4 Feb 2008 09:57:37 -0800 (PST) Organization: http://groups.google.com Message-ID: <48309f0b-8474-4973-aaba-50b5d63f0abd@m34g2000hsb.googlegroups.com> References: NNTP-Posting-Host: 66.126.103.122 Mime-Version: 1.0 Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: 7bit X-Trace: posting.google.com 1202147859 3756 127.0.0.1 (4 Feb 2008 17:57:39 GMT) X-Complaints-To: groups-abuse@google.com NNTP-Posting-Date: Mon, 4 Feb 2008 17:57:39 +0000 (UTC) Complaints-To: groups-abuse@google.com Injection-Info: m34g2000hsb.googlegroups.com; posting-host=66.126.103.122; posting-account=duW0ogkAAABjRdnxgLGXDfna0Gc6XqmQ User-Agent: G2/1.0 X-HTTP-UserAgent: Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.7.12) Gecko/20050922 Fedora/1.7.12-1.3.1,gzip(gfe),gzip(gfe) Xref: g2news1.google.com comp.lang.ada:19706 Date: 2008-02-04T09:57:37-08:00 List-Id: 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