erich@kgw2.bwi.WEC.COM (Eric Hammond) (03/07/90)
My thanks go out to those who responded to my post requesting help in implementing condition_wait()s with timeouts. In alphabetical order: Eric.Cooper@CS.CMU.EDU Richard.Draves@CS.CMU.EDU Richard H. E. Eiger <rheiger@ccng.waterloo.edu> Alessandro.Forin@SPICE.CS.CMU.EDU Arul A. Menezes <arul@Neon.Stanford.EDU> Michael_Young@transarc.com A rough summary of the problem is as follows: I need to do a condition_wait() on a condition, but want that condition to be signalled after n seconds (if and only if it hasn't been signalled already). Only one thread will ever be waiting on this condition, and only one thread will ever signal this condition. This condition and the waiting thread might dissappear after the condition is signalled. There are multiple thread/condition pairs in the same task that need this capability. Following is a list of possible solutions. Incorporated into these are summaries of suggestions I've received: Let thread A be a thread which wants to wait on the condition. Let thread T be a "timeout thread". 1) Before thread A waits on the condition it forks a new thread T. Thread T sleeps for n seconds then signals the condition. pro: - Simple. con: - Uses system resources (?): one thread created per condition. - In my application the condition might have been deallocated by the time thread T wakes up. - In my application thread A might have decided to do another condition wait with another timeout while thread T is still sleeping. Then when thread T wakes up it will signal the condition before the timeout now in effect. 2) Same as (1) but add a sequence number to each timeout. When thread T wakes up it checks to see if the sequence number is the same as when it was forked. If so, it signals the condition. If not, it simply exits. pro: - Solves third con in (1). con: - Still uses one thread per condition. - Still might signal a deallocated condition. 3) Create one timer thread T (per task) which will handle all timeouts. This thread will provide services by receiving requests on a port. One request will be to start a condition wait with timeout. Another will be to signal a condition. The timer thread will keep a queue of conditions being waited for in order of the time they will timeout. (Implement this in the standard method with each node indicating its timeout time and the length of time between its timeout and the timeout of the next node on the queue.) A timeout will then be added to the msg_receive() call corresponding to the timeout of the first condition wait in the queue. When a condition times out it will be signalled and removed from the queue. The same will be done when a request to signal a condition is received on the message port. pro: - Uses only one thread per task. - Will never signal a deallocated condition. - Will never signal a condition after it has already been signalled. con: - More complex to implement. 4) Same as (3), but why use condition variables at all? Have the condition wait implemented as a msg_rpc() and when the "condition" should be "signalled" simply reply to the rpc. pro: - Simpler than (3). 5) Don't use another thread at all. Implement conditions using Mach ports and messages. Here is a correlation between the concepts in conditions and what should be used to implement them. condition_t - port_name_t condition_alloc() - port_allocate() condition_free() - port_deallocate() condition_signal() - msg_send() condition_wait() - msg_receive() [ with timeout ] pro: - Fairly easy to implement. - No extra threads. - Solves all problems stated above. con: - When nobody is waiting on a real condition signalling it has no effect. Using this scheme a message is placed on the port's queue. This con might be solved a couple of ways: a. Clear the queue before doing a msg_receive(). (What about the queue filling up, though?) b. Since a msg_send() to PORT_NULL has no effect (?) store a copy of the name of the allocated port and msg_send() to this copy. Between calls to msg_receive() set this copy to PORT_NULL. And, of course, any of the above should be written in such a way that the actual implementation is hidden from the user. I'm going to be attacking the problem using (5) and (5b). I am not absolutely sure about the comment I made " a msg_send() to PORT_NULL has no effect". The worst I could see it doing would be to return the "invalid port" error which could be ignored. I'd like to hear from someone who knows. If anyone has further suggestions or comments about these or other solutions, I am still interested in hearing them. Eric. -- Eric Hammond @ Xetron Corporation, Cincinnati, Ohio erich@kgw2.bwi.wec.com
Richard.Draves@CS.CMU.EDU (03/10/90)
> Excerpts from netnews.comp.os.mach: 6-Mar-90 summary: timeout on > conditi.. Eric Hammond@kgw2.bwi.WE (5567) > I'm going to be attacking the problem using (5) and (5b). I am not > absolutely sure about the comment I made " a msg_send() to PORT_NULL > has no effect". The worst I could see it doing would be to return > the "invalid port" error which could be ignored. I'd like to hear > from someone who knows. Sending a message to PORT_NULL will result in a SEND_INVALID_PORT error. The msg_send will have no effect. (This is not true of all msg-send errors. For example, if there is a problem somewhere in the body of a message, and a previously parsed piece of the message sent out-of-line memory with the deallocate option, then the memory will be deallocated when the msg-send returns the error.) If you plan to use msg_receive with a timeout, and you care about the accuracy of the timeouts, there are some "features" you should be aware of. If a msg_receive is interrupted, say because of a signal, then the system call is interrupted and restarted unless the RCV_INTERRUPT option is used. However, when the msg-receive is restarted, it resumes with the original timeout. So if you do a receive with a 10 second timeout, and have a 1 second interval timer, the receive will never timeout because it will restart every second with another full 10 second timeout. If this is a problem for you, you should use the RCV_INTERRUPT option so you regain control when the msg-receive is interrupted (with a RCV_INTERUPTED return code), and then you can calculate the correct timeout value for the retried msg-receive. Rich