| Author |
Message |
|
| LeHoff |
Posted: Wed Jun 27, 2007 3:10 pm |
|
|
|
User
Joined: 28 Nov 2006
Posts: 38
|
I have written a nice C port in order to interface to some hardware - this is coming along nicely, but I have run into an issue with a timeout that I could use some ideas on how to handle.
The set-up: there is a server running on the hardware and I trying to get a message from the server in a blocking mode, i.e., the call on the C side does not succeed before there is a message received from the server.
The problem is that the gen_server:call that is calling the C port does a timeout with this tuple:
Code: {'EXIT',{timeout,{gen_server,...}}}
and then I am left with a dangling C port that is waiting for a message from the server on the hardware and an Erlang program that has lost all contact with the C port.
One way of avoiding this would be to use non-blocking getting of the message, but this becomes a pain since you then have to start looking at polling scenarios.
My C port does not have a state - it is merely a way to communicate with the server on the hardware, so I would prefer if I could handle the exit gracefully by restarting the C port, but I do not know how to tell a hanging C port to die so restarting is out of the question
Any suggestions? |
|
|
| Back to top |
|
| Mazen |
Posted: Wed Jun 27, 2007 8:00 pm |
|
|
|
User
Joined: 20 Jul 2006
Posts: 164
Location: London
|
Hi LeHoff
You can avoid the timeout completely by simply passing the atom infinity when you make a call to the genserver.
See gen_server:call/3 in the documentation (http://www.erlang.org/doc/man/gen_server.html Look for the Timeout parameter).
Hope this helps
/M
LeHoff wrote: I have written a nice C port in order to interface to some hardware - this is coming along nicely, but I have run into an issue with a timeout that I could use some ideas on how to handle.
The set-up: there is a server running on the hardware and I trying to get a message from the server in a blocking mode, i.e., the call on the C side does not succeed before there is a message received from the server.
The problem is that the gen_server:call that is calling the C port does a timeout with this tuple:
Code: {'EXIT',{timeout,{gen_server,...}}}
and then I am left with a dangling C port that is waiting for a message from the server on the hardware and an Erlang program that has lost all contact with the C port.
One way of avoiding this would be to use non-blocking getting of the message, but this becomes a pain since you then have to start looking at polling scenarios.
My C port does not have a state - it is merely a way to communicate with the server on the hardware, so I would prefer if I could handle the exit gracefully by restarting the C port, but I do not know how to tell a hanging C port to die so restarting is out of the question
Any suggestions? |
|
|
| Back to top |
|
| LeHoff |
Posted: Wed Jun 27, 2007 8:24 pm |
|
|
|
User
Joined: 28 Nov 2006
Posts: 38
|
Hi Mazen.
The infinity option is nice, but I do not like it since it gives me the problem that my contact with my C port will be forever blocked in one process (should have stated that in the initial post, sorry).
It would be nicer to handle the timeout in an orderly manner since that rhymes better with standard Erlang coding.
I have given this some thought while I was driving home today and my options are
* Handle the timeout in the {'EXIT',... in an orderly mannner.
* Do the timeout myself using an unblocking get message call to the hardware server.
Once implemented there is almost no difference between the two apart from the fact that the C port is never re-started in the second scenario and that there will quite a lot of polling while waiting for the first message or a timeout.
The second scenario has another advantage: if I by any chance should find myself wanting to do a real blocking get message I will only have one Erlang process sitting and waiting and the other proceses can continue to communicate with the hardware in between the polling from the "blocker".
Thanks for reminding me about the infinity option - it made me see the problem in a new light!
Cheers,
Torben
aka the odd Dane that took the OTP class with you last October. |
|
|
| Back to top |
|
| Mazen |
Posted: Wed Jun 27, 2007 8:52 pm |
|
|
|
User
Joined: 20 Jul 2006
Posts: 164
Location: London
|
Hi again (The odd dane ),
So if I understand you correctly you don't want the whole gen_server to be blocking by one process.
Maybe the option to return {no_reply, State} could help? This will allow the gen_server to continue processing incoming calls. However, in order to return a value to the process that has been blocked you have to use reply/2 where the first argument is the same as the second argument in handle_call/3 (The From variable).
Keep in mind that since the call to the C port is blocking (hence it won't allow the handle_call/3 function to return {no_reply, State}) you have to spawn a new process that handles the communication with the port. When this process returns from the port, it calls reply/2 which will return to the originating process.
so a scenario can look like this:
1) process A calls the gen_server
2) gen_server receives the request, and spawns process B to communicate with the port. It then returns {no_reply, State}. (Remember that process B needs to know the Pid of process A so that it can reply to it through reply/2)
3) the gen_server continues serving any potential requests while process A is blocking waiting for an answer
4) process B returns from the port with a result and calls reply(PidOfProcessA, DataFromPort) and then terminates.
5) Process A continues execution with the result of the gen_server call it made.
You will have 1 extra process for each request, but since 1 is blocking, it will not affect the performance (IF number of processes and efficiency need to be considered).
Also note that you should always pass the From variable as a reference back... since simply the pid might not be enough. It all depends on how the gen_server has chosen to create it's references and might change without notice in the future...
let me know if I'm not clear enough... It feels like I'm babbling... but I hope it makes sense  |
|
|
| Back to top |
|
| LeHoff |
Posted: Thu Jun 28, 2007 2:46 pm |
|
|
|
User
Joined: 28 Nov 2006
Posts: 38
|
Mazen, you are clear enough.
My problem is that if I allow my C port to block (and it does block!) I have no way of restoring my connection to the hardware server but to kill the C port and restart it.
That is not so bad in itself, but it means that for some 5000 ms (if using standard timeout in gen_server:call) all other processes will not be able to communicate with the hardware server - this is Bad (TM).
I have now implemented a poller that every N ms polls for a message and it stops when there is a real message or the timeout ms has gone by. It works like a charm and I will rather live with a slight performance degradation than block the hardware server.
Cheers,
Torben aka happy Erlang C port implementor! |
|
|
| Back to top |
|
|
|