unofficial mirror of guile-user@gnu.org 
 help / color / mirror / Atom feed
* Re: guile fibers - looking for adivce
  2020-09-06  0:47 guile fibers - looking for adivce Jan Wielkiewicz
@ 2020-09-06  0:42 ` Zelphir Kaltstahl
  2020-09-07 16:34   ` Jan Wielkiewicz
  2020-09-06  1:19 ` Aleix Conchillo Flaqué
  2020-09-06 19:26 ` Chris Vine
  2 siblings, 1 reply; 10+ messages in thread
From: Zelphir Kaltstahl @ 2020-09-06  0:42 UTC (permalink / raw)
  To: tona_kosmicznego_smiecia; +Cc: guile-user

Hi Jan!

On 9/6/20 2:47 AM, Jan Wielkiewicz wrote:
> Hello,
>
> I'm still on my way of integrating Guile Fibers and GOOPS, but I
> encountered some weird behavior.
> Say I have an object with with one slot being a vector and two methods
> accessing the vector concurrently/parallelly. The methods
> accessing/editing the vector are time consuming and are meant to be ran
> frequently. Assuming I would like to run the methods on separate
> fibers, how do I make sure the state of the object doesn't
> suddenly change during execution of a method? I would like the methods
> to be able to edit the state of the object, but in a cooperative manner.
Call me uneducated, but I believe, if you are accessing shared memory,
in this case the shared member vector of the object, you will have to
resort to things like locking mechanisms. I don't know of any other way,
except for not using shared memory and keeping one copy per fiber or per
concurrently running execution unit.
> The weird behavior I've encountered is that sometimes when running the
> first method (A) and right after starting the second method (B), the
> method B tries to access the vector before the method A finishes its
> job. What should I do to make accessing the vector cooperatively, yet
> to run the time consuming on separate fibers for concurrency?
>
> I was thinking about implementing slots in objects as sending and
> receiving messages instead of regular variables, but still there were
> some problems with the object state.
>
> Could someone explain me what should I do?

Is there a way you could avoid sharing the object? Perhaps copying it
for each fiber?

If not, then perhaps there is yet another way. A while ago I read some
article, which had the heading "pull not push" or something. So instead
of having multiple concurrent execution units trying to update the
vector, perhaps you could have multiple concurrent execution units,
which are each responsible for some exclusive part of the state of a
data structure and working on their own parts of the memory of it and
update it by retrieving information from the system. Perhaps this is
clearer, if I link to the article I read:

https://nullprogram.com/blog/2020/04/30/

This might only work in cases, where the data lends itself to such
"other way around" of updating state.

> Thanks in advance
> Jan Wielkiewicz

Out of ideas for now.

Regards,
Zelphir





^ permalink raw reply	[flat|nested] 10+ messages in thread

* guile fibers - looking for adivce
@ 2020-09-06  0:47 Jan Wielkiewicz
  2020-09-06  0:42 ` Zelphir Kaltstahl
                   ` (2 more replies)
  0 siblings, 3 replies; 10+ messages in thread
From: Jan Wielkiewicz @ 2020-09-06  0:47 UTC (permalink / raw)
  To: guile-user

Hello,

I'm still on my way of integrating Guile Fibers and GOOPS, but I
encountered some weird behavior.
Say I have an object with with one slot being a vector and two methods
accessing the vector concurrently/parallelly. The methods
accessing/editing the vector are time consuming and are meant to be ran
frequently. Assuming I would like to run the methods on separate
fibers, how do I make sure the state of the object doesn't
suddenly change during execution of a method? I would like the methods
to be able to edit the state of the object, but in a cooperative manner.

The weird behavior I've encountered is that sometimes when running the
first method (A) and right after starting the second method (B), the
method B tries to access the vector before the method A finishes its
job. What should I do to make accessing the vector cooperatively, yet
to run the time consuming on separate fibers for concurrency?

I was thinking about implementing slots in objects as sending and
receiving messages instead of regular variables, but still there were
some problems with the object state.

Could someone explain me what should I do?


Thanks in advance
Jan Wielkiewicz



^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: guile fibers - looking for adivce
  2020-09-06  0:47 guile fibers - looking for adivce Jan Wielkiewicz
  2020-09-06  0:42 ` Zelphir Kaltstahl
@ 2020-09-06  1:19 ` Aleix Conchillo Flaqué
  2020-09-07 16:56   ` Jan Wielkiewicz
  2020-09-06 19:26 ` Chris Vine
  2 siblings, 1 reply; 10+ messages in thread
From: Aleix Conchillo Flaqué @ 2020-09-06  1:19 UTC (permalink / raw)
  To: Jan Wielkiewicz; +Cc: guile-user

Hi!

On Sat, Sep 5, 2020 at 3:48 PM Jan Wielkiewicz <
tona_kosmicznego_smiecia@interia.pl> wrote:

> Hello,
>
> I'm still on my way of integrating Guile Fibers and GOOPS, but I
> encountered some weird behavior.
> Say I have an object with with one slot being a vector and two methods
> accessing the vector concurrently/parallelly. The methods
> accessing/editing the vector are time consuming and are meant to be ran
> frequently. Assuming I would like to run the methods on separate
> fibers, how do I make sure the state of the object doesn't
> suddenly change during execution of a method? I would like the methods
> to be able to edit the state of the object, but in a cooperative manner.
>
> The weird behavior I've encountered is that sometimes when running the
> first method (A) and right after starting the second method (B), the
> method B tries to access the vector before the method A finishes its
> job. What should I do to make accessing the vector cooperatively, yet
> to run the time consuming on separate fibers for concurrency?
>
> I was thinking about implementing slots in objects as sending and
> receiving messages instead of regular variables, but still there were
> some problems with the object state.
>
> Could someone explain me what should I do?
>
>
I believe sharing the same object by the two fibers defeats the purpose of
message passing, because with the way you are doing it you find exactly the
problem you are having and the only way to fix this is by using mutexes.
That is, you are trying to use fibers as you would use regular threads
sharing a resource. Instead, there should probably be a main fiber (that
holds the object) and send messages (with the data needed from the object)
to 2 other fibers (those would be your methods A and B).

I haven't used fibers extensively, but I've used Go quite a bit which
follows the same pattern of channels and message passing and this is how
you would do it.

Also, the fibers manual also suggests this approach:
https://github.com/wingo/fibers/wiki/Manual#33-mutation

That said, I might be wrong though.

Hope this helps,

Aleix


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: guile fibers - looking for adivce
  2020-09-06  0:47 guile fibers - looking for adivce Jan Wielkiewicz
  2020-09-06  0:42 ` Zelphir Kaltstahl
  2020-09-06  1:19 ` Aleix Conchillo Flaqué
@ 2020-09-06 19:26 ` Chris Vine
  2 siblings, 0 replies; 10+ messages in thread
From: Chris Vine @ 2020-09-06 19:26 UTC (permalink / raw)
  To: guile-user

On Sun, 6 Sep 2020 02:47:57 +0200
Jan Wielkiewicz <tona_kosmicznego_smiecia@interia.pl> wrote:
> Hello,
> 
> I'm still on my way of integrating Guile Fibers and GOOPS, but I
> encountered some weird behavior.
> Say I have an object with with one slot being a vector and two methods
> accessing the vector concurrently/parallelly. The methods
> accessing/editing the vector are time consuming and are meant to be ran
> frequently. Assuming I would like to run the methods on separate
> fibers, how do I make sure the state of the object doesn't
> suddenly change during execution of a method? I would like the methods
> to be able to edit the state of the object, but in a cooperative manner.
> 
> The weird behavior I've encountered is that sometimes when running the
> first method (A) and right after starting the second method (B), the
> method B tries to access the vector before the method A finishes its
> job. What should I do to make accessing the vector cooperatively, yet
> to run the time consuming on separate fibers for concurrency?
> 
> I was thinking about implementing slots in objects as sending and
> receiving messages instead of regular variables, but still there were
> some problems with the object state.
> 
> Could someone explain me what should I do?

You should use channels.



^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: guile fibers - looking for adivce
  2020-09-06  0:42 ` Zelphir Kaltstahl
@ 2020-09-07 16:34   ` Jan Wielkiewicz
  0 siblings, 0 replies; 10+ messages in thread
From: Jan Wielkiewicz @ 2020-09-07 16:34 UTC (permalink / raw)
  To: Zelphir Kaltstahl; +Cc: guile-user

Hi!

Dnia 2020-09-06, o godz. 02:42:20
Zelphir Kaltstahl <zelphirkaltstahl@posteo.de> napisał(a):

> Hi Jan!
>
> Call me uneducated, but I believe, if you are accessing shared memory,
> in this case the shared member vector of the object, you will have to
> resort to things like locking mechanisms. I don't know of any other
> way, except for not using shared memory and keeping one copy per
> fiber or per concurrently running execution unit.
I'll probably go with some locking mechanism then.

> Is there a way you could avoid sharing the object? Perhaps copying it
> for each fiber?
Fortunately, at least for now, one procedure updates the array and the
other only reads it. We'll see how scalable is my idea when the project
grows. 

> If not, then perhaps there is yet another way. A while ago I read some
> article, which had the heading "pull not push" or something. So
> instead of having multiple concurrent execution units trying to
> update the vector, perhaps you could have multiple concurrent
> execution units, which are each responsible for some exclusive part
> of the state of a data structure and working on their own parts of
> the memory of it and update it by retrieving information from the
> system. Perhaps this is clearer, if I link to the article I read:
Sounds interesting, thanks. Maybe I'll find use for this later.

> https://nullprogram.com/blog/2020/04/30/
> 
> This might only work in cases, where the data lends itself to such
> "other way around" of updating state.
> 
> > Thanks in advance
> > Jan Wielkiewicz
> 
> Out of ideas for now.
> 
> Regards,
> Zelphir
> 
> 

Anyway, thanks for ideas.

Jan Wielkiewicz



^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: guile fibers - looking for adivce
  2020-09-06  1:19 ` Aleix Conchillo Flaqué
@ 2020-09-07 16:56   ` Jan Wielkiewicz
  2020-09-08  0:25     ` Aleix Conchillo Flaqué
  0 siblings, 1 reply; 10+ messages in thread
From: Jan Wielkiewicz @ 2020-09-07 16:56 UTC (permalink / raw)
  To: Aleix Conchillo Flaqué; +Cc: guile-user

Hi!

Dnia 2020-09-05, o godz. 18:19:49
Aleix Conchillo Flaqué <aconchillo@gmail.com> napisał(a):

> Hi!
> 
> On Sat, Sep 5, 2020 at 3:48 PM Jan Wielkiewicz <
> tona_kosmicznego_smiecia@interia.pl> wrote:
> 
> I believe sharing the same object by the two fibers defeats the
> purpose of message passing, because with the way you are doing it you
> find exactly the problem you are having and the only way to fix this
> is by using mutexes. That is, you are trying to use fibers as you
> would use regular threads sharing a resource. Instead, there should
> probably be a main fiber (that holds the object) and send messages
> (with the data needed from the object) to 2 other fibers (those would
> be your methods A and B).
I see, thank you for explanation. This allowed me to understand the
problem doesn't lie in the way I was trying to implement my messaging
through fibers, but rather in the way I was using GOOPS - my custom
method for initializing slots called next-method after doing everything
what caused some weird problems with allocation.

Are threads and mutexes worth using, if there are fibers already? 
I know I'll have to learn this eventually, to fully master programming,
but I'm not sure if there's a great advantage over fibers.

> 
> I haven't used fibers extensively, but I've used Go quite a bit which
> follows the same pattern of channels and message passing and this is
> how you would do it.
> 
> Also, the fibers manual also suggests this approach:
> https://github.com/wingo/fibers/wiki/Manual#33-mutation
Yeah, I've read the manual like three times already, but Andy is
somethimes writing sentences from outer space, including the GOOPS
manual. This isn't the level of English I'm currently on.
I guess I'll write some self-explaining examples of GOOPS after I fully
understand it.

> That said, I might be wrong though.
> 
> Hope this helps,
> 
> Aleix

Thanks for explanation, this made my thinking much cleaner. I can get
back to experimenting now!


Jan Wielkiewicz



^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: guile fibers - looking for adivce
  2020-09-07 16:56   ` Jan Wielkiewicz
@ 2020-09-08  0:25     ` Aleix Conchillo Flaqué
  2020-09-08  7:48       ` Chris Vine
  2020-09-10  1:05       ` Jan Wielkiewicz
  0 siblings, 2 replies; 10+ messages in thread
From: Aleix Conchillo Flaqué @ 2020-09-08  0:25 UTC (permalink / raw)
  To: Jan Wielkiewicz; +Cc: guile-user

Hi Jan,

On Mon, Sep 7, 2020 at 7:56 AM Jan Wielkiewicz <
tona_kosmicznego_smiecia@interia.pl> wrote:

> Hi!
>
> Dnia 2020-09-05, o godz. 18:19:49
> Aleix Conchillo Flaqué <aconchillo@gmail.com> napisał(a):
>
> > Hi!
> >
> > On Sat, Sep 5, 2020 at 3:48 PM Jan Wielkiewicz <
> > tona_kosmicznego_smiecia@interia.pl> wrote:
> >
> > I believe sharing the same object by the two fibers defeats the
> > purpose of message passing, because with the way you are doing it you
> > find exactly the problem you are having and the only way to fix this
> > is by using mutexes. That is, you are trying to use fibers as you
> > would use regular threads sharing a resource. Instead, there should
> > probably be a main fiber (that holds the object) and send messages
> > (with the data needed from the object) to 2 other fibers (those would
> > be your methods A and B).
> I see, thank you for explanation. This allowed me to understand the
> problem doesn't lie in the way I was trying to implement my messaging
> through fibers, but rather in the way I was using GOOPS - my custom
> method for initializing slots called next-method after doing everything
> what caused some weird problems with allocation.
>
> Are threads and mutexes worth using, if there are fibers already?
> I know I'll have to learn this eventually, to fully master programming,
> but I'm not sure if there's a great advantage over fibers.
>
>
I don't think mixing threads and fibers is a good idea, so I would stick
with fibers. However, you might still want to use mutexes for certain
things.

This might be useful: https://github.com/golang/go/wiki/MutexOrChannel

As they say, sometimes you start over-using channels because it's fun, but
a mutex might just be simpler. So if you find yourself getting with too
many channels think if a mutex would be easier, and the other way around,
if using the mutex is getting too hairy try to think if a channel would be
easier.

I would suggest reading about Go routines and channels, since it's the same
as fibers and there's a lot of information out there.

> >
> > I haven't used fibers extensively, but I've used Go quite a bit which
> > follows the same pattern of channels and message passing and this is
> > how you would do it.
> >
> > Also, the fibers manual also suggests this approach:
> > https://github.com/wingo/fibers/wiki/Manual#33-mutation
> Yeah, I've read the manual like three times already, but Andy is
> somethimes writing sentences from outer space, including the GOOPS
> manual. This isn't the level of English I'm currently on.
> I guess I'll write some self-explaining examples of GOOPS after I fully
> understand it.
>
>
To be honest, I've never used GOOPS so things might be a bit more
complicated there, I don't know. But it sounds like you have two options:

- Create a fiber with the object and pass data from the object using
channels to other fibers. Then return data to the main fiber (or the fiber
that has the object) through a channel and update your object.
- Make the object global and have fibers that update the object. In this
case you would need to use mutexes.

Or maybe you find another way?

> That said, I might be wrong though.
> >
> > Hope this helps,
> >
> > Aleix
>
> Thanks for explanation, this made my thinking much cleaner. I can get
> back to experimenting now!
>
>
Cool, let us know!
Aleix


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: guile fibers - looking for adivce
  2020-09-08  0:25     ` Aleix Conchillo Flaqué
@ 2020-09-08  7:48       ` Chris Vine
  2020-09-08 17:56         ` Aleix Conchillo Flaqué
  2020-09-10  1:05       ` Jan Wielkiewicz
  1 sibling, 1 reply; 10+ messages in thread
From: Chris Vine @ 2020-09-08  7:48 UTC (permalink / raw)
  To: guile-user

On Mon, 7 Sep 2020 17:25:38 -0700
Aleix Conchillo Flaqué <aconchillo@gmail.com> wrote:
[snip]
> To be honest, I've never used GOOPS so things might be a bit more
> complicated there, I don't know. But it sounds like you have two options:
> 
> - Create a fiber with the object and pass data from the object using
> channels to other fibers. Then return data to the main fiber (or the fiber
> that has the object) through a channel and update your object.
> - Make the object global and have fibers that update the object. In this
> case you would need to use mutexes.
> 
> Or maybe you find another way?

The OP should use channels.  It is a bad idea to mix mutexes (which are
a construct for native OS threads) with fibers, because fibers run as
delimited continuations, a group of which may be running on a
particular thread.  To synchronize fibers, you use channels.

Guile's atomic variables (boxes) are available and can be used with
fibers because they do not block, but to use them you really need to
know what you are doing, just as you do if you are using them in C.
Atomic variables do synchronize memory, but they do not provide mutual
exclusion (they don't block) and so require expert skills: don't use
them if you don't know how to use atomic compare-and-swap correctly.

This is what the guile fibers manual has to say:

"Guile’s mutexes are an even worse solution with a Fibers system. It is
a bad idea for a fiber to grab a Guile mutex, because if the mutex is
not available, Guile will suspend not just the fiber that is running
but the entire kernel thread.  If the mutex is available, the fiber
obtains it, cool; but if it the fiber suspends while holding a mutex,
that’s bad news.  Any fiber trying to acquire a mutex while a suspended
fiber from the same thread already has the mutex will result in an
error: as Guile thinks that the mutex has already been acquired by the
current thread, it detects recursion and bails out.

...

"The root of this problem is that Guile associates mutexes with kernel
threads, not fibers. It would be possible however to make a
Fibers-appropriate implementation of mutexes, but we suggest that users
try atomic boxes or channels instead. If you do use mutexes, make sure
you disable preemption (possibly by a local call to
call-with-blocked-asyncs), and take care to never suspend a fiber while
it owns any mutex."



^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: guile fibers - looking for adivce
  2020-09-08  7:48       ` Chris Vine
@ 2020-09-08 17:56         ` Aleix Conchillo Flaqué
  0 siblings, 0 replies; 10+ messages in thread
From: Aleix Conchillo Flaqué @ 2020-09-08 17:56 UTC (permalink / raw)
  To: Chris Vine; +Cc: guile-user

On Tue, Sep 8, 2020 at 12:48 AM Chris Vine <vine35792468@gmail.com> wrote:

> On Mon, 7 Sep 2020 17:25:38 -0700
> Aleix Conchillo Flaqué <aconchillo@gmail.com> wrote:
> [snip]
> > To be honest, I've never used GOOPS so things might be a bit more
> > complicated there, I don't know. But it sounds like you have two options:
> >
> > - Create a fiber with the object and pass data from the object using
> > channels to other fibers. Then return data to the main fiber (or the
> fiber
> > that has the object) through a channel and update your object.
> > - Make the object global and have fibers that update the object. In this
> > case you would need to use mutexes.
> >
> > Or maybe you find another way?
>
> The OP should use channels.  It is a bad idea to mix mutexes (which are
> a construct for native OS threads) with fibers, because fibers run as
> delimited continuations, a group of which may be running on a
> particular thread.  To synchronize fibers, you use channels.
>
> Guile's atomic variables (boxes) are available and can be used with
> fibers because they do not block, but to use them you really need to
> know what you are doing, just as you do if you are using them in C.
> Atomic variables do synchronize memory, but they do not provide mutual
> exclusion (they don't block) and so require expert skills: don't use
> them if you don't know how to use atomic compare-and-swap correctly.
>
> This is what the guile fibers manual has to say:
>
> [snip]
>
>
Ooops, duh... Thank you Chris! I don't know what I was thinking when I
wrote that, I guess just about Go. Funny enough, I read that fibers section
about mutexes the day before, so it clearly didn't stick.

So, Jan, ignore my last email and stick to channels. My apologies for the
confusion.

Aleix


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: guile fibers - looking for adivce
  2020-09-08  0:25     ` Aleix Conchillo Flaqué
  2020-09-08  7:48       ` Chris Vine
@ 2020-09-10  1:05       ` Jan Wielkiewicz
  1 sibling, 0 replies; 10+ messages in thread
From: Jan Wielkiewicz @ 2020-09-10  1:05 UTC (permalink / raw)
  To: Aleix Conchillo Flaqué; +Cc: guile-user

Hello,

Dnia 2020-09-07, o godz. 17:25:38
Aleix Conchillo Flaqué <aconchillo@gmail.com> napisał(a):

> Hi Jan,
> 
> To be honest, I've never used GOOPS so things might be a bit more
> complicated there, I don't know. But it sounds like you have two
> options:
> 
> - Create a fiber with the object and pass data from the object using
> channels to other fibers. Then return data to the main fiber (or the
> fiber that has the object) through a channel and update your object.
> - Make the object global and have fibers that update the object. In
> this case you would need to use mutexes.
> 
> Or maybe you find another way?
> 
> > That said, I might be wrong though.
> > >
> > > Hope this helps,
> > >
> > > Aleix
> >
> > Thanks for explanation, this made my thinking much cleaner. I can
> > get back to experimenting now!
> >
> >
> Cool, let us know!
> Aleix

I actually made a working prototype. Basically I created a new
allocation option for GOOPS slots - #:message.
The default allocation type is #:instance, as explained here:
https://www.gnu.org/software/guile/manual/html_node/Slot-Options.html
The #:message allocation type I introduced treats setting a slot like
sending a message and reading the slot like receiving a message.
I used the example from GOOPS manual as inspiration:
https://www.gnu.org/software/guile/manual/html_node/Customizing-Class-Definition.html

Here's the code. It introduces a new metaclass
<message-allocation-metaclass> to modify the default behavior and
introduces the #:message allocation type.

(define-class <message-allocation-metaclass> (<class>))

(define-method (compute-get-n-set (class
<message-allocation-metaclass>) s) (define set-channel (make-channel))
  (define get-channel (make-channel))
  (define stored-value '())
  (define initialized? #f)
  (define (value-updater ch)
    (spawn-fiber
     (lambda ()
       (let loop ()
         (set! stored-value (get-message ch))
         (loop)))))
  (case (slot-definition-allocation s)
    ((#:message)
     (begin
       (list (lambda (o)
               (if initialized?
                   (begin
                     (spawn-fiber
                      (lambda ()
                        (put-message get-channel stored-value)))
                     (get-message get-channel))))
             (lambda (o v)
               (spawn-fiber
                (lambda ()
                  (if (not initialized?)
                      (begin
                        (set! initialized? #t)
                        (value-updater set-channel)))
                  (put-message set-channel v)))))))
    (else (next-method))))

Using this in practice looks something like this:

(define-class <dog> ()
  (number-of-legs #:setter set-number-of-legs
                  #:getter get-number-of-legs
                  #:allocation #:message)
  (toys #:setter set-toys
        #:getter get-toys
        #:allocation #:message)
  #:metaclass <message-allocation-metaclass>)

(define (main)
  (run-fibers
   (lambda ()
     (define dog1 (make <dog>))
     (spawn-fiber
      (lambda ()
        (set-number-of-legs dog1 4)
        (display "number of legs:\n")
        (display (get-number-of-legs dog1 4))
        (set-toys dog1 (list 'bone 'dead-rat 'something)))))
   #:drain? #t))

(main)

This solution allows setting and getting values safely over fibers
through messages, eliminating the problem of parallely editing the same
slot of an object (thanks to just one fiber being able to edit the real
value (value-updater)). If I'm not missing something important, this is
a real solution for a real problem.
I've already tested it with my toy program.
If you have any suggestions, feel free to tell me, also feel free to
use my code, if you find it useful.

I could push this a bit further by making the
<message-allocation-metaclass> allocate all slots by default
as #:message - this way every object would work safely on fibers,
without the boilerplate code.


Thanks for help, everyone. 

Jan Wielkiewicz



^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2020-09-10  1:05 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-09-06  0:47 guile fibers - looking for adivce Jan Wielkiewicz
2020-09-06  0:42 ` Zelphir Kaltstahl
2020-09-07 16:34   ` Jan Wielkiewicz
2020-09-06  1:19 ` Aleix Conchillo Flaqué
2020-09-07 16:56   ` Jan Wielkiewicz
2020-09-08  0:25     ` Aleix Conchillo Flaqué
2020-09-08  7:48       ` Chris Vine
2020-09-08 17:56         ` Aleix Conchillo Flaqué
2020-09-10  1:05       ` Jan Wielkiewicz
2020-09-06 19:26 ` Chris Vine

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).