On 09/15/2015 08:26 PM, Vincent Povirk wrote:
The part I'm not sure about yet is how we notify the first process that their object is now shared. My most sane thought for doing this (I have plenty of others :) is adding a more generic mechanism to tell the client "I have a message for you", but both SIGUSR1 and 2 are both being used. The handler for SIGUSR1 could be modified so that it first asks the server what the message is, which may be a "suspend thread" or "migrate object to server." So before proceeding, I would like to know your preference on how this should be handled.
FWIW, in our discussion on #winehackers, I was assuming you'd do the equivalent of InterlockedExchange in shared memory (between wineserver and the client) that contains the object state.
Well, Alexandre doesn't like that approach. I still think that it's doable to manage such a thing and still have the wineserver know when something isn't right and respond accordingly and I would still love to be able to convince Alexandre of that! :)
The client would notice as soon as it tried to do anything with the object that the object was moved to the server, and the server would know the previous state from the result of InterlockedExchange. (This assumes every valid object state, including a state indicating that it's in the server, can fit in 64 bits.)
I think that the idea Alexandre proposed is that the object will not use shared memory, so the "moving to the server" scenario occurs when a complex WaitForMultipleObjects (i.e., with mixed object types) is requested or another process gains access to the object.
I'm not sure it's a good idea for the wineserver to have to wait on code in a windows process (which, if the process is really hosed, might never respond) before it can reply to a DuplicateHandle request.
Well, the wineserver can't actually block, but somewhere we have to cause the calling thread of the second process to block until the object is migrated to the sever. I think that there's 3 ways to do this.
1. When the second process makes a call to get a handle to the (previously private) object, block the calling thread (delay the response) until the object is migrated to the server, or 2. When the second process makes a call to get a handle to the (previously private) object, trigger a migration but don't block the calling thread (pending a completed migration) until it makes a call to actually *use* that object (ReleaseSemaphore, QuerySemaphore, server_select, etc.), or 3. Don't do anything until the second process makes a call to actually use the object, then trigger the migration and wait for it to finish.
For simplicity, I was heading for the first option, but greater throughput could be obtained by the second option (if this is happening a lot) and a process will have a greater chance of getting to use an optimized (local) object with the 3rd option (not that I think it's a good option, just trying to be complete).
The only time you'd need to signal the client is when a thread is waiting on the object being moved, so that the thread can restart the wait using the server.
Ah, another good reason to share memory between client & server! :) Also, it would enable wait multiple with mixed object types w/o forcing the object to be migrated to the server too.
I don't know how you plan to wait on process-local objects, but perhaps you can perform the wait in such a way that the server can interrupt it easily, without disturbing the thread if it's doing anything else?
I don't have a complete design for this yet. I'm thinking that a custom synchronization mechanism using futexes would be appropriate (not that I've done that before or even used futexes) so we just notify a futex. Without the shared memory, I was thinking of having the server send SIGUSR1 (or some such) to the client and that handler would send a request to the server to see what it wanted. The server would reply by telling it to migrate some object and then the client would manage this from the handler. So with just kill(), I don't think you get a choice over which thread responds to it, so the handler would just need to be aware if it was actually waiting on a futex when it was signaled and manage that appropriately. Again, I haven't really worked that part out just yet.
Using shared memory is certainly another option -- then we just have the server mark the object as needing migration and signal a futex. When a wait is completed on the client, it should just always read that value to know why it was signaled.