Regarding the C zip: the relevant files for my example here are: bindings/ip_connection.(c|h) and bindings/bricklet_temperature.(c|h). ip_connection.(c|h) implements all the thread and socket logic. bricklet_temperature.(c|h) does the packing and unpacking of TCP/IP packages to function call arguments and return values. The C code forms a binding for a TCP/IP protocol here. The examples I provided are using this binding. All the threading and socket handling is done inside the binding C code.
Sorry, you're right my description of the spec is long and probably hard to follow. Let me try to summarize the critical points:
The C binding provides functions to talk to the hardware using TCP/IP. The C binding takes care of packing function call parameters and send them to the hardware and unpacking received data from the hardware to function call return values. There are other hardware modules beside the temperature sensor. For example, there is a relay module. It has a set_state function. The C binding takes care of creating the correct TCP/IP packet to set the state of the relay and sends it. all the user hast to do is calling the set_state function.
You're right: if all I wanted to do was setting a relay state or getting the temperature then this could all be done synchronous. The get_temperature function would just create and send request and then directly receive incoming data from the socket to get the response and return it to the user.
Okay, until here everything is fine, I just need a socket, that's it, no threads, no queues, no problems.
But there is also a button module. A program that wants to react on a button press could just call the is_pressed function over and over again to do that. Or it can tell the button module to send a message each time the button state changes. This is a callback, a spontaneous response send by the hardware without a corresponding request from the user program. There is no point in caching this information, as suggested.
And that'a what you called "mixing together two different APIs". There is the getter/setter style stuff and the callback stuff. One could argue that all information that callbacks can provide can also be polled for by getter calls. That's basically correct from a general point of view. But from a hardware point of view polling wastes a lot of bandwidth especially for events that done occur that often such as the press of a button. Also the hardware might not be directly connected to TCP/IP, there might be low bandwidth connections such as USB and RS485 in between. From an API and protocol point of view the actual electrical connection is not visible to the user program. There is also the problem that most communication links are designed for high throughput, but this protocol requires low latency due to the request/response nature. So callback are useful and have advantages compared to polling.
The callbacks is what makes the implementation of the protocol difficult here, but the hardware and the TCP/IP protocol is there and fixed, the task is to implement a Perl module that provides these features to the user.
To receive callbacks there has to be someone receiving incoming data from the socket all the time. This cannot be done within the simple model of only receiving data from the socket if a response for a getter call is expected. The C code does this by using a receive thread (thread 1: ip_connection.c:1284 ipcon_receive_loop) that constantly receives incoming data. Then the callbacks have to be delivered to the user code. This is done by the callback thread (thread 2: ip_connection.c:1141 ipcon_callback_loop). As explained in the earlier post, this cannot be done from the receive thread if the user should be able to call getters from the callback functions. The last thread (thread 3) is the main-thread of the program or any other user-created thread that calls getter/setter function of the bindings. As getter/setter calls directly write their request to the socket. Communication between all this threads is done by queues.
Instead of using threads for callback handling the binding code could include a blocking handle_callbacks function that does the work of the receive and callback threads. Then the user has to call it in order to use callbacks. This is how this is realized in PHP, that lacked thread support at the time the PHP binding was implemented. But I'd like to have the Perl binding work as all the other bindings, and keep PHP as an exception.
This got quite long again. It seems that I fail at stating my problems in a short fashion :-)
In reply to Re^4: Socket client thread design problem
by photron
in thread Socket client thread design problem
by photron
| For: | Use: | ||
| & | & | ||
| < | < | ||
| > | > | ||
| [ | [ | ||
| ] | ] |