Racket threads and callbacks to another thread

I'm wondering how the Gui of racket works.

There must be some thread that processes events from the widget set (wxwidgets afaik).

However, callbacks of e.g. buttons are processed in the main (repl) thread.

How is this even possible without having some event listener in the main thread, and without blocking the main thread?

Have you seem "1.6 Event Dispatching and Eventspaces" ?

If I recall correctly, it answers (parts of?) the question.

I read it some time ago, but didn’t know if I could use it for my use case. I’m developing an integration with QtWebEngine(View) by “wire”, i.e. starting it as a process and communicating with it using stdin/out.

Now I’m looking for some way to dispatch events in the main thread. If that is even necessary(?)

So, in DrRacket I see:

> (event-dispatch-handler)
#<procedure:drscheme-event-dispatch-handler>

And in console racket:

Welcome to Racket v8.17 [cs].
> (require racket/gui)
> (event-dispatch-handler)
#<procedure:really-dispatch-event>
>

Does this mean that the REPL loop is replaced by some other loop when one requires racket/gui?

Do you plan to make Qt an alternative backend for racket/gui ?
In that case, you can use eventspaces.
If not, then I am not 100% sure whether eventspaces can be used or not.
My hunch is that they can't be used.

The DrRacket repl is not the same as the terminal one.
But I think the difference, you are seeing, is that DrRacket replaces the event-dispatch-handler in order to co-exist with gui applications run from DrRacket.

@robby will know how DrRacket handles eventspaces - and whether you can reuse them.

Well, not a full Qt backend, more like an electron interface.

Hi Axel,

Thank you for the pointer. This spike seems to do what I want:

;(define my-event-space (make-eventspace))

(define wins (make-hash))
(define win-nr 0)

(define events '())

(define (event win evt)
  (set! events (append events (list (list win evt)))))

(define-struct window
  (nr))

(define (mkwin)
  (parameterize ([current-eventspace (current-eventspace)]) ; my-event-space])
    (let ((nr (begin (set! win-nr (+ win-nr 1)) win-nr)))
      (hash-set! wins nr
                 (lambda (evt)
                   (displayln (format "handling event ~a ~a" evt (current-thread)))
                   (if (eq? (current-thread) the-thread)
                       (displayln "In REPL thread")
                       (displayln "Not the REPL thread"))
                   ))
      (make-window nr))))

(define (event-handler win event)
  (let ((win-handler (hash-ref wins win #f)))
    (if (eq? win-handler #f)
        (displayln (format "No such window ~a, cannot send event ~a" win event))
        (win-handler event))))

(define (evt-thread secs)
  (parameterize ([current-eventspace (current-eventspace)]) ; my-event-space])
    (thread (lambda ()
              (letrec ((f (lambda ()
                            (if (null? events)
                                (begin
                                  (displayln "null events")
                                  (sleep secs)
                                  (f))
                                (let* ((e (car events))
                                       (win (car e))
                                       (evt (cadr e))
                                       )
                                  (set! events (cdr events))
                                  (if (eq? evt 'quit)
                                      #t
                                      (begin
                                        (queue-callback (lambda ()
                                                          (displayln (format "callback ~a ~a" e (current-thread)))
                                                          (event-handler win evt)
                                                          #t))
                                        (displayln (format "queued ~a ~a" e (current-thread)))
                                        (yield)
                                        (f))))))))
                (f))))))

Some DrRacket output:

Welcome to DrRacket, version 8.17 [cs].
Language: racket/base [custom].
> (evt-thread 3)
#<threadnull events
:...testing/call-cc.rkt:164:12>
null events
> (define w (mkwin))
> w
#<window>
null events
null events
> (window-nr w)
1
null events
null events
null events
> (event 1 'hoi)
null events
queued (1 hoi) #<thread:...testing/call-cc.rkt:164:12>
null events
callback (1 hoi) #<thread:eventspace-handler-thread-proc>
handling event hoi #<thread:eventspace-handler-thread-proc>
In REPL thread
null events
null events
null events
null events
null events
> (event 1 'yes) (event 2 'no)
queued (1 yes) #<thread:...testing/call-cc.rkt:164:12>
queued (2 no) #<thread:...testing/call-cc.rkt:164:12>
callback (1 yes) #<thread:eventspace-handler-thread-proc>
handling event yes #<thread:eventspace-handler-thread-proc>
In REPL thread
null events
callback (2 no) #<thread:eventspace-handler-thread-proc>
No such window 2, cannot send event no
null events
null events
> (event 0 'quit)
> (current-thread)
#<thread:eventspace-handler-thread-proc>
> 

I'm not really sure what @hnmdijkema's goal is, but it is the case that one can dispatch events by calling queue-callback; that's sensitive to current-eventspace and it adds to the event queue which the handler thread for the event space will pick up when it gets to that event.

@hnmdijkema if you're just getting some description of events as a data structure and you want to send them along to the appropriate racket controls, you can probably just call queue-callback where the thunk you pass is the event handler. I can't quite tell what your code is doing, as it has free variables in it but you probably want to avoid a global that's accessed without a lock from multiple threads (if that's what you're doing) and probably also you want to avoid calling sleep.

I hope to show some results of my little project soon.

Awesome! I'm looking forward to it :smiley: