Fellow Schemers,
I have 3 event-related needs:
1. I need to push events from C++ to Scheme 2. I need to push events from Scheme to C++ 3. I need to setup timers within Scheme that would also generate events
I could have C++ call a Scheme function and have the Scheme function return data. This would cover 1 and 2 except I would need to pass a list of events back into Scheme.
Sometimes I need to pass events from Scheme to C++ this could be one event or it could be a few.
Last but not least, I need to generate an event (i.e. do something) when a timer expires. All my processing could be done in a blocking fashion if it weren't for timers.
For example, I would set up a timer while waiting for player action and if the timer expired I would assume that the player is dead and fold his cards (this is poker) and force him to leave the game (remove him).
I would also set up a timer after enough players gathered for a game, just to wait and see if someone else would join. If there's still enough players after the timer expires then I would start the game. Otherwise I would go back to waiting for players.
My main loop MUST run in C++ as for my server I'm using a C++ networking library and for my GUI client I'm using SDL which has its own event loop as well.
Something I could do is set up two queues (lists?) in Scheme, one for incoming events and one for outgoing. My scheme code would then run in some sort of a thread, polling the incoming queue and posting events to the outgoing queue. C++ would poll the outgoing queue and post events to the incoming queue via a Scheme call.
Writing Scheme bindings for the networking library and for SDL could be another option but I'd rather try to set up a thin C++ to Scheme interface layer first as it would let me focus on the application logic.
Any suggestions?
Thanks in advance, Joel
Afficher les réponses par date
Something I could do is set up two queues (lists?) in Scheme, one for incoming events and one for outgoing. My scheme code would then run in some sort of a thread, polling the incoming queue and posting events to the outgoing queue. C++ would poll the outgoing queue and post events to the incoming queue via a Scheme call.
Writing Scheme bindings for the networking library and for SDL could be another option but I'd rather try to set up a thin C++ to Scheme interface layer first as it would let me focus on the application logic.
Are you willing to start a C thread for the Scheme code? This would decouple control between Scheme and C, allowing timers to be implemented in Scheme with the I/O timeout mechanisms. Then you need two pipes between the C and Scheme worlds. These pipes could be actual OS pipes, sockets, or a new type of device (this would be more efficient and portable, but require the implementation of a new device in _os_io.c).
Marc
Marc Feeley wrote: Are you willing to start a C thread for the Scheme code?
I could but I'd rather avoid it if I can.
This would decouple control between Scheme and C, allowing timers to be implemented in Scheme with the I/O timeout mechanisms.
How would you implement these timers?
Then you need two pipes between the C and Scheme worlds. These pipes could be actual OS pipes, sockets, or a new type of device (this would be more efficient and portable, but require the implementation of a new device in _os_io.c).
Can they be simple Scheme lists?
I thought I could have 3 Scheme functions I would call from C++ where one would start the Scheme loop in a Scheme thread and the other two would let C++ post events (sexp) to the "incoming events" Scheme list and the other would retrieve them from the "outgoing" list. The post/retrieve functions would use a mutex to block access to the lists/queues. Then I could do the timer by launching another thread within Gambit.
Maybe I'm misunderstanding the nature of integration that would be required here. Please let me know!
Thanks, Joel
Marc Feeley wrote: Are you willing to start a C thread for the Scheme code?
I could but I'd rather avoid it if I can.
That is a problem because the timers will only expire on time if C returns control to Scheme in a timely manner. Polling should be avoided... A possible solution is for the Scheme world to tell the C world when its next scheduled timeout will be (i.e. the first suspended thread on the Scheme thread scheduler's timeout queue), and C has to call back to Scheme at that point at the lattest (so you have to manage one timer on the C side for this). There's currently no way to tell when the next scheduled timeout will be, but a Scheme function for this can easily be added to _thread.scm (5 lines of code).
This would decouple control between Scheme and C, allowing timers to be implemented in Scheme with the I/O timeout mechanisms.
How would you implement these timers?
Here's some sample code:
(define-type timer mutex )
(define (timer-start timeout thunk) (let* ((m (make-mutex)) (t (make-timer m))) (mutex-lock! m) ; make sure mutex is locked (thread-start! (make-thread (lambda () (if (mutex-lock! m timeout) ; block at most until timeout #f ; timer was stopped before the timeout (thunk))))) t))
(define (timer-stop timer) (mutex-unlock! (timer-mutex timer)))
(define t (timer-start 5 (lambda () (pp 'hello))))
;(timer-stop t) ; uncomment to stop timer prematurely
(let loop () (thread-sleep! 1) (pp '*) (loop))
Alternatively, you could put the timeout on some blocking I/O operation or synchronization operation (mutex, condition variable, thread join, etc) and get the equivalent of a timer.
Then you need two pipes between the C and Scheme worlds. These pipes could be actual OS pipes, sockets, or a new type of device (this would be more efficient and portable, but require the implementation of a new device in _os_io.c).
Can they be simple Scheme lists?
I thought I could have 3 Scheme functions I would call from C++ where one would start the Scheme loop in a Scheme thread and the other two would let C++ post events (sexp) to the "incoming events" Scheme list and the other would retrieve them from the "outgoing" list. The post/retrieve functions would use a mutex to block access to the lists/queues. Then I could do the timer by launching another thread within Gambit.
Maybe I'm misunderstanding the nature of integration that would be required here. Please let me know!
It can't just be a Scheme list. You need some synchronization mechanism. Ideally the Scheme side should just try to get an event and block until an event is available. A FIFO created with open-vector-pipe like this would do:
(receive (in out) (open-vector-pipe '(direction: input)) ; in is the input port side of the FIFO and out is the ; output port side of the FIFO (write 111 out) ; first event (write 222 out) ; second event (write 333 out) ; third event (force-output out) (list (read in) (read in))) ; returns (111 222)
So the C side should call a Scheme function to add an event to the FIFO (on the output port side), and this will unblock the Scheme thread that is blocked on a read on the input port side of the FIFO.
Marc
Marc Feeley wrote:
That is a problem because the timers will only expire on time if C returns control to Scheme in a timely manner. Polling should be avoided...
So if I don't need exact timers and make it a point of calling some Scheme function on every iteration of my C++ main loop (every second or so) then I should be ok, right?
And this would not be an issue if I just start a thread on the C++ side and then call a Scheme function that never returns (Scheme main loop), right?
So the C side should call a Scheme function to add an event to the FIFO (on the output port side), and this will unblock the Scheme thread that is blocked on a read on the input port side of the FIFO.
Just for posterity and the google archives, there's another excellent example of how to do this on page 122 of the Gambit manual.
Thanks a lot for your help!
Thanks, Joel