Hi all.
When an L4 server is started, its main thread goes into the server loop and blocks until a message from a client is received. When a message is received, a server loop calls a dispatch function, which calls functions, specific to RPC opcodes. The problem is that if one of these functions is blocked, the whole server loop is blocked.
In my case, I am implementing an API, which is called by a client program. This API starts a child process, and has an option to block the calling program, until the child is terminated. This API is called DosExecPgm(). So, the client program calls this API, then the API call blocks, and then the child must be executed, and finally, call another API, which is called DosExit(). But when DosExecPgm is called, the server loop is blocked, and it cannot receive a message for calling the next API, DosExit.
I tried to implement API call blocking using a semaphore. DosExecPgm from a parent process is waiting on the semaphore, and DosExit from a child process reset the semaphore.
So, the problem is with server loop blocking. So, I thought to make for DosExecPgm a dedicated server loop. which is blocked, but the main server loop continues to execute. But how to correctly make these server loops? There are many API's which can block, so, what can I do if not to dedicate a separate loop for each function? And, if there are many server loops, then there will be many threads executing them, so I must to register each thread on the name server. -- Then how can the calling program determine, which server thread to call for each API?
I feel that these problems have common solutions, but i'm in trouble -- maybe, someone will help with finding these common solutions?
Thanks in advance, WBR, valery
On Sat, 02 Apr 2011 12:58:34 +1300 (MSD), Valery V. Sedletski wrote:
Hi all.
...
I have rethought the problem and, it seems, found the solution -- still block on a semaphore, but not in server stub, but in the client routine which calls the RPC to the server.
But the problem remains -- the semaphore is in the server, but need to share it with the client for it to reset it. -- So the next question arises -- how to tell DICE to map the semaphore to the client? -- I have the semaphore in the server, and trying to return it from the RPC to the server. But DICE returns a copy of the semaphore, and not maps it to the client. I try to use the following IDL declaration:
APIRET dos_ExecPgm ([out, string, prealloc_client, prealloc_server] char **pObjname, [in] long cbObjname, [in] unsigned long execFlag, [in, string] char *pArg, [in, string] char *pEnv, [in, out] struct _RESULTCODES *pRes, [out, size_is(len), ref, prealloc_client, prealloc_server] l4semaphore_t **tSem, [out] int *len, [in, string] char *pName);
-- so, I'm trying to use 'ref' attribute for tSem. But in that case, DICE requires me to specify the array with 'len' parameter. Can I avoid this length parameter? (I must return one l4semaphore_t * value from the IDL method, and get the mapping of original semaphore, not its copy).
So, any help is appreciated.
WBR, valery
On Sat Apr 02, 2011 at 12:58:34 +1300, Valery V. Sedletski wrote:
When an L4 server is started, its main thread goes into the server loop and blocks until a message from a client is received. When a message is received, a server loop calls a dispatch function, which calls functions, specific to RPC opcodes. The problem is that if one of these functions is blocked, the whole server loop is blocked.
In my case, I am implementing an API, which is called by a client program. This API starts a child process, and has an option to block the calling program, until the child is terminated. This API is called DosExecPgm(). So, the client program calls this API, then the API call blocks, and then the child must be executed, and finally, call another API, which is called DosExit(). But when DosExecPgm is called, the server loop is blocked, and it cannot receive a message for calling the next API, DosExit.
I tried to implement API call blocking using a semaphore. DosExecPgm from a parent process is waiting on the semaphore, and DosExit from a child process reset the semaphore.
So, the problem is with server loop blocking. So, I thought to make for DosExecPgm a dedicated server loop. which is blocked, but the main server loop continues to execute. But how to correctly make these server loops? There are many API's which can block, so, what can I do if not to dedicate a separate loop for each function? And, if there are many server loops, then there will be many threads executing them, so I must to register each thread on the name server. -- Then how can the calling program determine, which server thread to call for each API?
I feel that these problems have common solutions, but i'm in trouble -- maybe, someone will help with finding these common solutions?
I think the generic solution is to maintain a couple of worker threads inside the server and delegate work to them, keeping the main server thread responsive. Another solution could be to have a thread per client.
On Mon Apr 04, 2011 at 09:22:07 +1300, Valery V. Sedletski wrote:
On Sat, 02 Apr 2011 12:58:34 +1300 (MSD), Valery V. Sedletski wrote:
Hi all.
...
I have rethought the problem and, it seems, found the solution -- still block on a semaphore, but not in server stub, but in the client routine which calls the RPC to the server.
But the problem remains -- the semaphore is in the server, but need to share it with the client for it to reset it. -- So the next question arises -- how to tell DICE to map the semaphore to the client? -- I have the semaphore in the server, and trying to return it from the RPC to the server. But DICE returns a copy of the semaphore, and not maps it to the client. I try to use the following IDL declaration:
APIRET dos_ExecPgm ([out, string, prealloc_client, prealloc_server] char **pObjname, [in] long cbObjname, [in] unsigned long execFlag, [in, string] char *pArg, [in, string] char *pEnv, [in, out] struct _RESULTCODES *pRes, [out, size_is(len), ref, prealloc_client, prealloc_server] l4semaphore_t **tSem, [out] int *len, [in, string] char *pName);
-- so, I'm trying to use 'ref' attribute for tSem. But in that case, DICE requires me to specify the array with 'len' parameter. Can I avoid this length parameter? (I must return one l4semaphore_t * value from the IDL method, and get the mapping of original semaphore, not its copy).
I think the l4semaphore was never intented to be used between two different tasks. Also, if you would do so, l4semaphore_t must be in shared memory between the two tasks, as the structure is for example maintaining a counter that is used in the up and down implementations. Thus you'd need to map the memory to the task, and have it placed on a separate page. I guess in your situation blocking on a worker thread in server could work.
Adam
On Mon, 4 Apr 2011 20:23:43 +0200, Adam Lackorzynski wrote:
On Sat Apr 02, 2011 at 12:58:34 +1300, Valery V. Sedletski wrote:
....
I feel that these problems have common solutions, but i'm in trouble -- maybe, someone will help with finding these common solutions?
I think the generic solution is to maintain a couple of worker threads inside the server and delegate work to them, keeping the main server thread responsive. Another solution could be to have a thread per client.
So, I could imagine, that the server thread, when receiving a message from a client, starts a child, then sends a message to one of its worker threads, then it blocks on a semaphore (which is local to the server). Then the server loop must continue, but not respond to the 1st client (which started the 2nd task) until the worker thread unblocks. -- The worker thread unblocks, and answers to the 1st client (which has been blocked), then it continues.
But how can I tell DICE to not immediately answer the 1st task? -- I checked my IDL methods, and see one of them having the
short *dice_reply;
parameter. -- I found that if it is equal to DICE_REPLY, then the server loop calls marshal function and replies to the client. But if it is equal to the DICE_NEVER_REPLY, then it doesn't reply. -- Maybe, I can use this feature, but how to force DICE to generate such a parameter? -- My other IDL methods haven't one... Can I specify some attribute to the method?
On Mon Apr 04, 2011 at 09:22:07 +1300, Valery V. Sedletski wrote:
On Sat, 02 Apr 2011 12:58:34 +1300 (MSD), Valery V. Sedletski wrote:
Hi all.
...
I have rethought the problem and, it seems, found the solution -- still block on a semaphore, but not in server stub, but in the client routine which calls the RPC to the server.
But the problem remains -- the semaphore is in the server, but need to share it with the client for it to reset it. -- So the next question arises -- how to tell DICE to map the semaphore to the client? -- I have the semaphore in the server, and trying to return it from the RPC to the server. But DICE returns a copy of the semaphore, and not maps it to the client. I try to use the following IDL declaration:
APIRET dos_ExecPgm ([out, string, prealloc_client, prealloc_server] char **pObjname, [in] long cbObjname, [in] unsigned long execFlag, [in, string] char *pArg, [in, string] char *pEnv, [in, out] struct _RESULTCODES *pRes, [out, size_is(len), ref, prealloc_client, prealloc_server] l4semaphore_t **tSem, [out] int *len, [in, string] char *pName);
-- so, I'm trying to use 'ref' attribute for tSem. But in that case, DICE requires me to specify the array with 'len' parameter. Can I avoid this length parameter? (I must return one l4semaphore_t * value from the IDL method, and get the mapping of original semaphore, not its copy).
I think the l4semaphore was never intented to be used between two different tasks. Also, if you would do so, l4semaphore_t must be in shared memory between the two tasks, as the structure is for example maintaining a counter that is used in the up and down implementations. Thus you'd need to map the memory to the task, and have it placed on a separate page.
I supposed that DICE can establish a temporary shared memory area around the semaphore, and the counters will update accordingly. (I used 'ref' attribute beside the semaphore).
I guess in your situation blocking on a worker thread in server could work.
But the situation 'out of worker threads' can occur, so 'Denial of service' can occur. -- I heard, L4 has a limitation of 127 threads per task, is it true?
WBR, valery
On Tue, 05 Apr 2011 16:45:12 +1300 (MSD), Valery V. Sedletski wrote:
But how can I tell DICE to not immediately answer the 1st task? -- I checked my IDL methods, and see one of them having the
short *dice_reply;
parameter. -- I found that if it is equal to DICE_REPLY, then the server loop calls marshal function and replies to the client. But if it is equal to the DICE_NEVER_REPLY, then it doesn't reply. -- Maybe, I can use this feature, but how to force DICE to generate such a parameter? -- My other IDL methods haven't one... Can I specify some attribute to the method?
ah, found why -- I that IDL method had the [allow_reply_only] attribute..
But is that the correct means to cause DICE to not answer?
WBR, valery
On Tue, 05 Apr 2011 16:58:52 +1300 (MSD), Valery V. Sedletski wrote:
ah, found why -- I that IDL method had the [allow_reply_only] attribute..
But is that the correct means to cause DICE to not answer?
Already found a section about asynchronous servers in DICE manual. And even, implemented such a technique in my program, so all goes well :)
Thank you, Adam, for your help!
WBR, valery
l4-hackers@os.inf.tu-dresden.de