Re: Bi-directional sub-process communication

2015-11-24 Thread Ian Kelly
On Mon, Nov 23, 2015 at 10:25 PM, Cameron Simpson  wrote:
> Then #3. I would have a common function/method for submitting a request to
> go to the subprocess, and have that method return an Event on which to wait.
> Then caller then just waits for the Event and collects the data. Obviously,
> the method does not just return the Event, but an Event and something to
> receive the return data. I've got a class called a Result for this kind of
> thing; make a small class containing an Event and which will have a .result
> attribute for the return information; the submitting method allocates one of
> these and returns it. The response handler gets the instance (by looking it
> up from the tag), sets the .result attribute and fires the Event. Your
> caller wakes up from waiting on the Event and consults the .result
> attribute.

Your Result sounds suspiciously like a Future. ;-)
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Bi-directional sub-process communication

2015-11-24 Thread Cameron Simpson

On 24Nov2015 06:33, israel  wrote:

On 11/23/2015 20:29, Cameron Simpson wrote:

On 24Nov2015 16:25, Cameron Simpson  wrote:

Completely untested example code:

class ReturnEvent:
 def __init__(self):
   self.event = Event()


With, of course:

  def wait(self):
return self.event.wait()


Of course :-) Ah, the Event() object comes from the threading module. 
That makes sense. This should work perfectly. Thanks so much for 
taking the time to help me out!


Glad to be of service,
Cameron Simpson 

All the doors in this ship have nice sunny dispositions. It is their pleasure 
to open for you, and their satisfaction to close with the knowledge of a job 
well done. - Marvin _The Hitchhiker's Guide to the Galaxy_

--
https://mail.python.org/mailman/listinfo/python-list


Re: Bi-directional sub-process communication

2015-11-24 Thread Cameron Simpson

On 24Nov2015 14:53, Ian Kelly  wrote:

On Mon, Nov 23, 2015 at 10:25 PM, Cameron Simpson  wrote:

Then #3. I would have a common function/method for submitting a request to
go to the subprocess, and have that method return an Event on which to wait.
Then caller then just waits for the Event and collects the data. Obviously,
the method does not just return the Event, but an Event and something to
receive the return data. I've got a class called a Result for this kind of
thing; make a small class containing an Event and which will have a .result
attribute for the return information; the submitting method allocates one of
these and returns it. The response handler gets the instance (by looking it
up from the tag), sets the .result attribute and fires the Event. Your
caller wakes up from waiting on the Event and consults the .result
attribute.


Your Result sounds suspiciously like a Future. ;-)


Yeah. I already had this stuff when the futures module was released, with some 
additional stuff futures didn't have (eg my dispatch queue is a priority queue, 
which is handy for some kinds of workflows; default dispatch is FIFO).


My commonest use case/instance is a LateFunction, returned from my Later class.  
Use:


 L = Later()
 LF = L.defer(cllable, *a, *kw)
 ...
 result = LF()

When you call a LateFunction you get the function result, blocking if it has 
not yet been run. It also raises if the deferred function raised. It has 
additional methods, but that is the core use: make it look like a function.


You can do other easy things like:

 L.after(LFs, callable, *a, **kw)

to have a function dispatched after the completion of other LateFunctions or:

 with L.ready():
   ... suite ...

to block until the Later has a slot, then run the suite.

My Result and LateFunctions are subclasses of an Asynchron base class, which 
lets you wait for the result to arrive and has the common methods (wait, etc).  
The Result class supports this:


 R = Result()

 # caller, blocks until value received
 value = R.result

 # worker: deliver result
 R.result = func(blah)

This makes thread cooperation far far more friendly.

Cheers,
Cameron Simpson 

Ride with a llama and you never ride alone.
- Jeff Earls, DoD #0530, 
--
https://mail.python.org/mailman/listinfo/python-list


Re: Bi-directional sub-process communication

2015-11-24 Thread israel

On 11/23/2015 20:29, Cameron Simpson wrote:

On 24Nov2015 16:25, Cameron Simpson  wrote:

Completely untested example code:

class ReturnEvent:
  def __init__(self):
self.event = Event()


With, of course:

   def wait(self):
 return self.event.wait()


Of course :-) Ah, the Event() object comes from the threading module. 
That makes sense. This should work perfectly. Thanks so much for taking 
the time to help me out!

-
Israel Brewster



Cheers,
Cameron Simpson 

Maintainer's Motto: If we can't fix it, it ain't broke.

--
https://mail.python.org/mailman/listinfo/python-list


Re: Bi-directional sub-process communication

2015-11-23 Thread Ian Kelly
On Mon, Nov 23, 2015 at 10:54 AM, Israel Brewster  wrote:
> Concern: Since the master process is multi-threaded, it seems likely enough 
> that multiple threads on the master side would make requests at the same 
> time. I understand that the Queue class has locks that make this fine (one 
> thread will complete posting the message before the next is allowed to 
> start), and since the child process only has a single thread processing 
> messages from the queue, it should process them in order and post the 
> responses (if any) to the master_queue in order. But now I have multiple 
> master processes all trying to read master_queue at the same time. Again, the 
> locks will take care of this and prevent any overlapping reads, but am I 
> guaranteed that the threads will obtain the lock and therefore read the 
> responses in the right order? Or is there a possibility that, say, thread 
> three will get the response that should have been for thread one? Is this 
> something I need to take into consideration, and if so, how?

Yes, if multiple master threads are waiting on the queue, it's
possible that a master thread could get a response that was not
intended for it. As far as I know there's no guarantee that the
waiting threads will be woken up in the order that they called get(),
but even if there are, consider this case:

Thread A enqueues a request.
Thread B preempts A and enqueues a request.
Thread B calls get on the response queue.
Thread A calls get on the response queue.
The response from A's request arrives and is given to B.

Instead of having the master threads pull objects off the response
queue directly, you might create another thread whose sole purpose is
to handle the response queue. That could look like this:


request_condition = threading.Condition()
response_global = None

def master_thread():
global response_global
with request_condition:
request_queue.put(request)
request_condition.wait()
# Note: the Condition should remain acquired until
response_global is reset.
response = response_global
response_global = None
if wrong_response(response):
raise RuntimeError("got a response for the wrong request")
handle_response(response)

def response_thread():
global response_global
while True:
response = response_queue.get()
with request_condition:
response_global = response
request_condition.notify()


As another option you could use a multiprocessing.Manager to
coordinate passing the response back more directly, but starting a
third process seems like overkill for this.
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Bi-directional sub-process communication

2015-11-23 Thread Israel Brewster
On Nov 23, 2015, at 12:45 PM, Cameron Simpson  wrote:
> 
> On 23Nov2015 12:22, Israel Brewster  wrote:
>> On Nov 23, 2015, at 11:51 AM, Ian Kelly  wrote:
>>> Concurrency, ugh.
> 
> I'm a big concurrency fan myself.
> 
>>> It's probably better just to have a Condition/Event per thread and
>>> have the response thread identify the correct one to notify, rather
>>> than just notify a single shared Condition and hope the threads wake
>>> up in the right order.
>> 
>> Tell me about it :-) I've actually never worked with conditions or 
>> notifications (actually even this bi-drectional type of communication is new 
>> to me), so I'll have to look into that and figure it out. Thanks for the 
>> information!
> 
> I include a tag with every request, and have the responses include the tag; 
> the request submission function records the response hander in a mapping by 
> tag and the response handing thread looks up the mapping and passes the 
> response to the right handler.
> 
> Works just fine and avoids all the worrying about ordering etc.
> 
> Israel, do you have control over the protocol between you and your 
> subprocess?  If so, adding tags is easy and effective.

I do, and the basic concept makes sense. The one difficulty I am seeing is 
getting back to the thread that requested the data.  Let me know if this makes 
sense or I am thinking about it wrong:

- When a thread requests some data, it sends the request as a dictionary 
containing a tag (unique to the thread) as well as the request
- When the child processes the request, it encodes the response as a dictionary 
containing the tag and the response data
- A single, separate thread on the "master" side parses out responses as they 
come in and puts them into a dictionary keyed by tag
- The requesting threads, after putting the request into the Queue, would then 
block waiting for data to appear under their key in the dictionary

Of course, that last step could be interesting - implementing the block in such 
a way as to not tie up the processor, while still getting the data "as soon" as 
it is available. Unless there is some sort of built-in notification system I 
could use for that? I.e. the thread would "subscribe" to a notification based 
on its tag, and then wait for notification. When the master processing thread 
receives data with said tag, it adds it to the dictionary and "publishes" a 
notification to that tag. Or perhaps the notification itself could contain the 
payload? 

Thanks for the information!

> 
> Cheers,
> Cameron Simpson 

-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Bi-directional sub-process communication

2015-11-23 Thread Chris Kaynor
On Mon, Nov 23, 2015 at 2:18 PM, Israel Brewster 
wrote:

> Of course, that last step could be interesting - implementing the block in
> such a way as to not tie up the processor, while still getting the data "as
> soon" as it is available. Unless there is some sort of built-in
> notification system I could use for that? I.e. the thread would "subscribe"
> to a notification based on its tag, and then wait for notification. When
> the master processing thread receives data with said tag, it adds it to the
> dictionary and "publishes" a notification to that tag. Or perhaps the
> notification itself could contain the payload?


There are a few ways I could see handling this, without having the threads
spinning and consuming CPU:

   1. Don't worry about having the follow-up code run in the same thread,
   and use a simple callback. This callback could be dispatched to a thread
   via a work queue, however you may not get the same thread as the one that
   made the request. This is probably the most efficient method to use, as the
   threads can continue doing other work while waiting for a reply, rather
   than blocking. It does make it harder to maintain state between the pre-
   and post-request functions, however.
   2. Have a single, global, event variable that wakes all threads waiting
   on a reply, each of which then checks to see if the reply is for it, or
   goes back to sleep. This is good if most of the time, only a few threads
   will be waiting for a reply, and checking if the correct reply came in is
   cheap. This is probably good enough, unless you have a LOT of threads
   (hundreds).
   3. Have an event per thread. This will use less CPU than the second
   option, however does require more memory and OS resources, and so will not
   be viable for huge numbers of threads, though if you hit the limit, you are
   probably using threads wrong.
   4. Have an event per request. This is only better than #3 if a single
   thread may make multiple requests at once, and can do useful work when any
   of them get a reply back (if they need all, it will make no difference).

Generally, I would use option #1 or #2. Option 2 has the advantage of
making it easy to write the functions that use the functionality, while
option 1 will generally use fewer resources, and allows threads to continue
to be used while waiting for replies. How much of a benefit that is depends
on exactly what you are doing.

Option #4 would probably be better implemented using option #1 in all cases
to avoid problems with running out of OS memory - threading features
generally require more limited OS resources than memory. Option #3 will
also often run into the same issues as option #4 in the cases it will
provide any benefit over option #2.

Chris
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Bi-directional sub-process communication

2015-11-23 Thread Israel Brewster
On Nov 23, 2015, at 1:43 PM, Chris Kaynor  wrote:
> 
> On Mon, Nov 23, 2015 at 2:18 PM, Israel Brewster 
> wrote:
> 
>> Of course, that last step could be interesting - implementing the block in
>> such a way as to not tie up the processor, while still getting the data "as
>> soon" as it is available. Unless there is some sort of built-in
>> notification system I could use for that? I.e. the thread would "subscribe"
>> to a notification based on its tag, and then wait for notification. When
>> the master processing thread receives data with said tag, it adds it to the
>> dictionary and "publishes" a notification to that tag. Or perhaps the
>> notification itself could contain the payload?
> 
> 
> There are a few ways I could see handling this, without having the threads
> spinning and consuming CPU:
> 
>   1. Don't worry about having the follow-up code run in the same thread,
>   and use a simple callback. This callback could be dispatched to a thread
>   via a work queue, however you may not get the same thread as the one that
>   made the request. This is probably the most efficient method to use, as the
>   threads can continue doing other work while waiting for a reply, rather
>   than blocking. It does make it harder to maintain state between the pre-
>   and post-request functions, however.
>   2. Have a single, global, event variable that wakes all threads waiting
>   on a reply, each of which then checks to see if the reply is for it, or
>   goes back to sleep. This is good if most of the time, only a few threads
>   will be waiting for a reply, and checking if the correct reply came in is
>   cheap. This is probably good enough, unless you have a LOT of threads
>   (hundreds).
>   3. Have an event per thread. This will use less CPU than the second
>   option, however does require more memory and OS resources, and so will not
>   be viable for huge numbers of threads, though if you hit the limit, you are
>   probably using threads wrong.
>   4. Have an event per request. This is only better than #3 if a single
>   thread may make multiple requests at once, and can do useful work when any
>   of them get a reply back (if they need all, it will make no difference).
> 
> Generally, I would use option #1 or #2. Option 2 has the advantage of
> making it easy to write the functions that use the functionality, while
> option 1 will generally use fewer resources, and allows threads to continue
> to be used while waiting for replies. How much of a benefit that is depends
> on exactly what you are doing.

While I would agree with #1 in general, the threads, in this case, are CherryPy 
threads, so I need to get the data and return it to the client in the same 
function call, which of course means the thread needs to block until the data 
is ready - it can't return and let the result be processed "later".

Essentially there are times that the web client needs some information that 
only the Child process has. So the web client requests the data from the master 
process, and the master process then turns around and requests the data from 
the child, but it needs to get the data back before it can return it to the web 
client. So it has to block waiting for the data.

Thus we come to option #2 (or 3), which sounds good but I have no clue how to 
implement :-) Maybe something like http://pubsub.sourceforge.net ? I'll dig 
into that.

> 
> Option #4 would probably be better implemented using option #1 in all cases
> to avoid problems with running out of OS memory - threading features
> generally require more limited OS resources than memory. Option #3 will
> also often run into the same issues as option #4 in the cases it will
> provide any benefit over option #2.
> 
> Chris
> -- 
> https://mail.python.org/mailman/listinfo/python-list

-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Bi-directional sub-process communication

2015-11-23 Thread Cameron Simpson

On 23Nov2015 12:22, Israel Brewster  wrote:

On Nov 23, 2015, at 11:51 AM, Ian Kelly  wrote:

Concurrency, ugh.


I'm a big concurrency fan myself.


It's probably better just to have a Condition/Event per thread and
have the response thread identify the correct one to notify, rather
than just notify a single shared Condition and hope the threads wake
up in the right order.


Tell me about it :-) I've actually never worked with conditions or 
notifications (actually even this bi-drectional type of communication is new 
to me), so I'll have to look into that and figure it out. Thanks for the 
information!


I include a tag with every request, and have the responses include the tag; the 
request submission function records the response hander in a mapping by tag and 
the response handing thread looks up the mapping and passes the response to the 
right handler.


Works just fine and avoids all the worrying about ordering etc.

Israel, do you have control over the protocol between you and your subprocess?  
If so, adding tags is easy and effective.


Cheers,
Cameron Simpson 
--
https://mail.python.org/mailman/listinfo/python-list


Re: Bi-directional sub-process communication

2015-11-23 Thread Cameron Simpson

On 24Nov2015 16:25, Cameron Simpson  wrote:

Completely untested example code:

class ReturnEvent:
  def __init__(self):
self.event = Event()


With, of course:

   def wait(self):
 return self.event.wait()

Cheers,
Cameron Simpson 

Maintainer's Motto: If we can't fix it, it ain't broke.
--
https://mail.python.org/mailman/listinfo/python-list


Re: Bi-directional sub-process communication

2015-11-23 Thread Cameron Simpson

On 23Nov2015 14:14, Israel Brewster  wrote:

On Nov 23, 2015, at 1:43 PM, Chris Kaynor  wrote:

On Mon, Nov 23, 2015 at 2:18 PM, Israel Brewster 
wrote:
  3. Have an event per thread. This will use less CPU than the second
  option, however does require more memory and OS resources, and so will not
  be viable for huge numbers of threads, though if you hit the limit, you are
  probably using threads wrong.

[...]
While I would agree with #1 in general, the threads, in this case, are 
CherryPy threads, so I need to get the data and return it to the client in the 
same function call, which of course means the thread needs to block until the 
data is ready - it can't return and let the result be processed "later".


Then #3. I would have a common function/method for submitting a request to go 
to the subprocess, and have that method return an Event on which to wait. Then 
caller then just waits for the Event and collects the data. Obviously, the 
method does not just return the Event, but an Event and something to receive 
the return data. I've got a class called a Result for this kind of thing; make 
a small class containing an Event and which will have a .result attribute for 
the return information; the submitting method allocates one of these and 
returns it. The response handler gets the instance (by looking it up from the 
tag), sets the .result attribute and fires the Event. Your caller wakes up from 
waiting on the Event and consults the .result attribute.


Completely untested example code:

 class ReturnEvent:
   def __init__(self):
 self.event = Event()

 seq = 0
 re_by_tag = {}

 def submit_request(query):
   global seq, re_by_tag
   tag = seq
   seq += 1
   RE = ReturnEvent()
   re_by_tag[tag] = RE
   send_request(tag, query)
   return RE

 def process_response(tag, response_data):
   RE = re_by_tag.pop(tag)
   RE.result = response_data
   RE.event.set()

 ... CherryPy request handler ...
 RE = submit_request(your_query_info)
 RE.wait()
 response_data = RE.result

Cheers,
Cameron Simpson 
--
https://mail.python.org/mailman/listinfo/python-list


Re: Bi-directional sub-process communication

2015-11-23 Thread Israel Brewster
On Nov 23, 2015, at 3:05 PM, Dennis Lee Bieber  wrote:
> 
> On Mon, 23 Nov 2015 08:54:38 -0900, Israel Brewster 
> declaimed the following:
> 
>> Concern: Since the master process is multi-threaded, it seems likely enough 
>> that multiple threads on the master side would make requests at the same 
>> time. I understand that the Queue class has locks that make
> 
>   Multiple "master" threads, to me, means you do NOT have a "master
> process".

But I do: the CherryPy "application", which has multiple threads - one per 
request (and perhaps a few more) to be exact. It's these request threads that 
generate the calls to the child process.

> 
>   Let there be a Queue for EVERY LISTENER.
> 
>   Send the Queue as part of the request packet.

No luck: "RuntimeError: Queue objects should only be shared between processes 
through inheritance"

This IS a master process, with multiple threads, trying to communicate with a 
child process. That said, with some modifications this sort of approach could 
still work.

---
Israel Brewster
Systems Analyst II
Ravn Alaska
5245 Airport Industrial Rd
Fairbanks, AK 99709
(907) 450-7293
---


> 
>   Let the subthread reply to the queue that was provided via the packet
> 
>   Voila! No intermixing of "master/slave" interaction; each slave only
> replies to the master that sent it a command; each master only receives
> replies from slaves it has commanded. Slaves can still be shared, as they
> are given the information of which master they need to speak with.
> 
>   
> 
> -- 
>   Wulfraed Dennis Lee Bieber AF6VN
>wlfr...@ix.netcom.comHTTP://wlfraed.home.netcom.com/
> 
> -- 
> https://mail.python.org/mailman/listinfo/python-list

-- 
https://mail.python.org/mailman/listinfo/python-list


Bi-directional sub-process communication

2015-11-23 Thread Israel Brewster
I have a multi-threaded python app (CherryPy WebApp to be exact) that launches 
a child process that it then needs to communicate with bi-driectionally. To 
implement this, I have used a pair of Queues: a child_queue which I use for 
master->child communication, and a master_queue which is used for child->master 
communication.

The way I have the system set up, the child queue runs a loop in a tread that 
waits for messages on child_queue, and when received responds appropriately 
depending on the message received, which sometimes involves posting a message 
to master_queue.

On the master side, when it needs to communicate with the child process, it 
posts a message to child_queue, and if the request requires a response it will 
then immediately start waiting for a message on master_queue, typically with a 
timeout.

While this process works well in testing, I do have one concern (maybe 
unfounded) and a real-world issue

Concern: Since the master process is multi-threaded, it seems likely enough 
that multiple threads on the master side would make requests at the same time. 
I understand that the Queue class has locks that make this fine (one thread 
will complete posting the message before the next is allowed to start), and 
since the child process only has a single thread processing messages from the 
queue, it should process them in order and post the responses (if any) to the 
master_queue in order. But now I have multiple master processes all trying to 
read master_queue at the same time. Again, the locks will take care of this and 
prevent any overlapping reads, but am I guaranteed that the threads will obtain 
the lock and therefore read the responses in the right order? Or is there a 
possibility that, say, thread three will get the response that should have been 
for thread one? Is this something I need to take into consideration, and if so, 
how?

Real-world problem: While as I said this system worked well in testing, Now 
that I have gotten it out into production I've occasionally run into a problem 
where the master thread waiting for a response on master_queue times out while 
waiting. This causes a (potentially) two-fold problem, in that first off the 
master process doesn't get the information it had requested, and secondly that 
I *could* end up with an "orphaned" message on the queue that could cause 
problems the next time I try to read something from it.

I currently have the timeout set to 3 seconds. I can, of course, increase that, 
but that could lead to a bad user experience - and might not even help the 
situation if something else is going on. The actual exchange is quite simple:

On the master side, I have this code:

config.socket_queue.put('GET_PORT')
try:
port = config.master_queue.get(timeout=3)  #wait up to three seconds for a 
response
except Empty:
 port = 5000  # default. Can't hurt to try.

Which, as you might have been able to guess, tries to ask the child process (an 
instance of a tornado server, btw) what port it is listening on. The child 
process then, on getting this message from the queue, runs the following code:

elif item == 'GET_PORT':
  port = utils.config.getint('global', 'tornado.port')
  master_queue.put(port)

So nothing that should take any significant time. Of course, since this is a 
single thread handling any number of requests, it is possible that the thread 
is tied up responding to a different request (or that the GIL is preventing the 
thread from running at all, since another thread might be commandeering the 
processor), but I find it hard to believe that it could be tied up for more 
than three seconds.

So is there a better way to do sub-process bi-directional communication that 
would avoid these issues? Or do I just need to increase the timeout (or remove 
it altogether, at the risk of potentially causing the thread to hang if no 
message is posted)? And is my concern justified, or just paranoid? Thanks for 
any information that can be provided!

---
Israel Brewster
Systems Analyst II
Ravn Alaska
5245 Airport Industrial Rd
Fairbanks, AK 99709
(907) 450-7293
---




-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Bi-directional sub-process communication

2015-11-23 Thread Ian Kelly
On Mon, Nov 23, 2015 at 12:55 PM, Ian Kelly  wrote:
> On Mon, Nov 23, 2015 at 10:54 AM, Israel Brewster  
> wrote:
>> Concern: Since the master process is multi-threaded, it seems likely enough 
>> that multiple threads on the master side would make requests at the same 
>> time. I understand that the Queue class has locks that make this fine (one 
>> thread will complete posting the message before the next is allowed to 
>> start), and since the child process only has a single thread processing 
>> messages from the queue, it should process them in order and post the 
>> responses (if any) to the master_queue in order. But now I have multiple 
>> master processes all trying to read master_queue at the same time. Again, 
>> the locks will take care of this and prevent any overlapping reads, but am I 
>> guaranteed that the threads will obtain the lock and therefore read the 
>> responses in the right order? Or is there a possibility that, say, thread 
>> three will get the response that should have been for thread one? Is this 
>> something I need to take into consideration, and if so, how?
>
> Yes, if multiple master threads are waiting on the queue, it's
> possible that a master thread could get a response that was not
> intended for it. As far as I know there's no guarantee that the
> waiting threads will be woken up in the order that they called get(),
> but even if there are, consider this case:
>
> Thread A enqueues a request.
> Thread B preempts A and enqueues a request.
> Thread B calls get on the response queue.
> Thread A calls get on the response queue.
> The response from A's request arrives and is given to B.
>
> Instead of having the master threads pull objects off the response
> queue directly, you might create another thread whose sole purpose is
> to handle the response queue. That could look like this:
>
>
> request_condition = threading.Condition()
> response_global = None
>
> def master_thread():
> global response_global
> with request_condition:
> request_queue.put(request)
> request_condition.wait()
> # Note: the Condition should remain acquired until
> response_global is reset.
> response = response_global
> response_global = None
> if wrong_response(response):
> raise RuntimeError("got a response for the wrong request")
> handle_response(response)
>
> def response_thread():
> global response_global
> while True:
> response = response_queue.get()
> with request_condition:
> response_global = response
> request_condition.notify()

Actually I realized that this fails because if two threads get
notified at about the same time, they could reacquire the Condition in
the wrong order and so get the wrong responses.

Concurrency, ugh.

It's probably better just to have a Condition/Event per thread and
have the response thread identify the correct one to notify, rather
than just notify a single shared Condition and hope the threads wake
up in the right order.
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Bi-directional sub-process communication

2015-11-23 Thread Israel Brewster
On Nov 23, 2015, at 11:51 AM, Ian Kelly  wrote:
> 
> On Mon, Nov 23, 2015 at 12:55 PM, Ian Kelly  wrote:
>> On Mon, Nov 23, 2015 at 10:54 AM, Israel Brewster  
>> wrote:
>>> Concern: Since the master process is multi-threaded, it seems likely enough 
>>> that multiple threads on the master side would make requests at the same 
>>> time. I understand that the Queue class has locks that make this fine (one 
>>> thread will complete posting the message before the next is allowed to 
>>> start), and since the child process only has a single thread processing 
>>> messages from the queue, it should process them in order and post the 
>>> responses (if any) to the master_queue in order. But now I have multiple 
>>> master processes all trying to read master_queue at the same time. Again, 
>>> the locks will take care of this and prevent any overlapping reads, but am 
>>> I guaranteed that the threads will obtain the lock and therefore read the 
>>> responses in the right order? Or is there a possibility that, say, thread 
>>> three will get the response that should have been for thread one? Is this 
>>> something I need to take into consideration, and if so, how?
>> 
>> Yes, if multiple master threads are waiting on the queue, it's
>> possible that a master thread could get a response that was not
>> intended for it. As far as I know there's no guarantee that the
>> waiting threads will be woken up in the order that they called get(),
>> but even if there are, consider this case:
>> 
>> Thread A enqueues a request.
>> Thread B preempts A and enqueues a request.
>> Thread B calls get on the response queue.
>> Thread A calls get on the response queue.
>> The response from A's request arrives and is given to B.
>> 
>> Instead of having the master threads pull objects off the response
>> queue directly, you might create another thread whose sole purpose is
>> to handle the response queue. That could look like this:
>> 
>> 
>> request_condition = threading.Condition()
>> response_global = None
>> 
>> def master_thread():
>>global response_global
>>with request_condition:
>>request_queue.put(request)
>>request_condition.wait()
>># Note: the Condition should remain acquired until
>> response_global is reset.
>>response = response_global
>>response_global = None
>>if wrong_response(response):
>>raise RuntimeError("got a response for the wrong request")
>>handle_response(response)
>> 
>> def response_thread():
>>global response_global
>>while True:
>>response = response_queue.get()
>>with request_condition:
>>response_global = response
>>request_condition.notify()
> 
> Actually I realized that this fails because if two threads get
> notified at about the same time, they could reacquire the Condition in
> the wrong order and so get the wrong responses.
> 
> Concurrency, ugh.
> 
> It's probably better just to have a Condition/Event per thread and
> have the response thread identify the correct one to notify, rather
> than just notify a single shared Condition and hope the threads wake
> up in the right order.

Tell me about it :-) I've actually never worked with conditions or 
notifications (actually even this bi-drectional type of communication is new to 
me), so I'll have to look into that and figure it out. Thanks for the 
information!

> -- 
> https://mail.python.org/mailman/listinfo/python-list

-- 
https://mail.python.org/mailman/listinfo/python-list