guile-user
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

guile-fibers questions


From: Caleb Ristvedt
Subject: guile-fibers questions
Date: Thu, 30 May 2019 06:32:51 -0500
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/26.2 (gnu/linux)

(Not sure if this is the right place to ask ¯\_(ツ)_/¯)

I'm in the position of wanting to use guile-fibers for a server (with
the usual "make a new fiber for each new client" pattern), but my
"communication with the outside world" goes beyond reading and writing
on ports - I actually have to do some synchronization with fcntl file
locks. As far as I can tell from reading the epoll(2) man page, there's
no simple way to "wait" on acquiring that kind of lock at the same time
as waiting for file descriptors to become readable / writable. It's
possible to do a nonblocking attempt at acquiring a lock and yield to
the scheduler if it fails, then try again next time, but that basically
amounts to busy-waiting.

The first potential solution I thought of was just creating a new kernel
thread for the sole purpose of being blocked and then waking up the
fiber and terminating. This could be done easily enough with channels, I
imagine, along these lines:

--------------------------------------------
(define (acquire-lock lock fd)
  (let ((acquired-rendezvous (make-channel)))
    ;; What's the fibers way of specifically creating a new kernel thread?
    (call-with-new-thread
     (lambda ()
       ;; should loop in case of EINTR...
       (put-message acquired-rendezvous (fcntl fd F_SETLKW lock))))
    ;; This fiber suspends and the scheduler resumes another fiber while we wait
    ;; for above thread to finish
    (get-message acquired-rendezvous)))
--------------------------------------------

But it seems pretty strange to me - kernel threads are supposed to have
pretty high overhead to create, too, although I suppose it'd be possible
to only resort to creating one if the first nonblocking attempt at
acquiring the lock fails. The Fibers manual mentions thread pools as a
possible solution - would that involve basically just keeping a pool of
these "waiter threads" around instead of terminating them after they
finish waiting?

Also, I'm a bit confused by the terminology. In particular, I don't
really understand what is meant by "scheduler" in the manual. In several
places it seems to be synonymous with "kernel thread" (for example,
#:parallelism in run-fibers "arranges for a number of peer schedulers to
also be created", by default in total current-processor-count). And in
the internals interface, make-scheduler apparently returns one object,
but once again #:parallelism apparently makes it possible to make
multiple?

Would this version of the above code be mostly-equivalent?

--------------------------------------------
(define (acquire-lock lock fd)
  (let ((waiter-sched (make-scheduler #:parallelism 1))
        (acquired-rendezvous (make-channel)))
    (spawn-fiber
     (lambda ()
       ;; should loop on fcntl in case of EINTR...
       (put-message acquired-rendezvous (fcntl fd F_SETLKW lock)))
     waiter-sched)
    (get-message acquired-rendezvous)))
--------------------------------------------

Thanks.

- reepca



reply via email to

[Prev in Thread] Current Thread [Next in Thread]