Boost sprint: #3407 boost::call_once not re-entrant (at least in win32)

Hi, I have changed the ticket from threads to thread As this works as expected, Anthony could just add on the documentation why reentrant call_once functions are not a good idea. The explanation of Steven could be a good starting point: "I'll also point out that there is no good behavior for a recursive call to call_once. If it calls the function again, then f will be called twice. If it does not, then whatever initialization that call_once is doing may be incomplete. In either case, you have to know whether a call can be recursive and carefully design for it. If it happens accidentally, you're probably toast regardless of how it behaves. In the face of recursive calls it is impossible to maintain the post-condition that the function has run exactly once." After this modification IMO the ticket can be closed. Best, Vicente -- View this message in context: http://old.nabble.com/Boost-sprint%3A--3407-boost%3A%3Acall_once-not-re-entr... Sent from the Boost - Dev mailing list archive at Nabble.com.

On Sun, Nov 22, 2009 at 11:49 AM, Vicente Botet Escriba <vicente.botet@wanadoo.fr> wrote:
Hi,
I have changed the ticket from threads to thread
As this works as expected, Anthony could just add on the documentation why reentrant call_once functions are not a good idea. The explanation of Steven could be a good starting point:
"I'll also point out that there is no good behavior for a recursive call to call_once. If it calls the function again, then f will be called twice. If it does not, then whatever initialization that call_once is doing may be incomplete. In either case, you have to know whether a call can be recursive and carefully design for it. If it happens accidentally, you're probably toast regardless of how it behaves. In the face of recursive calls it is impossible to maintain the post-condition that the function has run exactly once."
After this modification IMO the ticket can be closed.
Best, Vicente
First of all, I agree that a recursive call_once makes little sense. However, I was in the process of writing a (better?) version of call_once that avoided creating the system wide mutex (on Windows), using a process-local Event instead (and even that is not created unless there is contention - ie the second thread in creates the event). While tackling this, I also decided to tackle ways of handling exceptions (should 'f' be recalled if the first attempt threw an exception?), as well as optionally allowing recursion. The code is currently Windows-only, although it could be ported most anywhere. I'd be interested to know: - if the exception handling and/or recursion handling are worthwhile options and more importantly - if the avoidance of creating a named mutex is worthwhile (I think so) Code attached. Not 100% tested yet (or 'boostified'), but I'm pretty sure the algorithm is sound. Tony

Gottlob Frege <gottlobfrege@gmail.com> writes:
On Sun, Nov 22, 2009 at 11:49 AM, Vicente Botet Escriba <vicente.botet@wanadoo.fr> wrote:
As this works as expected, Anthony could just add on the documentation why reentrant call_once functions are not a good idea. The explanation of Steven could be a good starting point:
"I'll also point out that there is no good behavior for a recursive call to call_once. If it calls the function again, then f will be called twice. If it does not, then whatever initialization that call_once is doing may be incomplete. In either case, you have to know whether a call can be recursive and carefully design for it. If it happens accidentally, you're probably toast regardless of how it behaves. In the face of recursive calls it is impossible to maintain the post-condition that the function has run exactly once."
After this modification IMO the ticket can be closed.
Thanks Vicente, I agree. I've updated the docs.
However, I was in the process of writing a (better?) version of call_once that avoided creating the system wide mutex (on Windows), using a process-local Event instead (and even that is not created unless there is contention - ie the second thread in creates the event). While tackling this, I also decided to tackle ways of handling exceptions (should 'f' be recalled if the first attempt threw an exception?), as well as optionally allowing recursion.
The code is currently Windows-only, although it could be ported most anywhere. I'd be interested to know:
- if the exception handling and/or recursion handling are worthwhile options
I don't believe so.
and more importantly - if the avoidance of creating a named mutex is worthwhile (I think so)
Yes, that's a worthwhile goal; creating such a mutex is not cheap.
Code attached. Not 100% tested yet (or 'boostified'), but I'm pretty sure the algorithm is sound.
Crikey that's a lot of code for a simple feature! I would just go for adding a "running" state to the flag value. If the flag is not "complete", set it to "running" using CAS. If we set it, then run, otherwise create an event, store it in the once_flag object (using CAS in case another waiter already stored an event), check the flag is still "running" and then wait for our event. When the event is triggered, loop again (to run the function if it threw last time). Once the thread that runs the function has completed the function call it can then set the flag to "complete", and set the event if there is one. If the function throws then the flag can be set back to "not started" and the event set if there is one. Anthony -- Author of C++ Concurrency in Action | http://www.stdthread.co.uk/book/ just::thread C++0x thread library | http://www.stdthread.co.uk Just Software Solutions Ltd | http://www.justsoftwaresolutions.co.uk 15 Carrallack Mews, St Just, Cornwall, TR19 7UL, UK. Company No. 5478976

Anthony Williams wrote:
Gottlob Frege <gottlobfrege@gmail.com> writes:
- if the exception handling and/or recursion handling are worthwhile options
I don't believe so.
Exceptions need to be handled properly. Recursion does not.

"Peter Dimov" <pdimov@pdimov.com> writes:
Anthony Williams wrote:
Gottlob Frege <gottlobfrege@gmail.com> writes:
- if the exception handling and/or recursion handling are worthwhile options
I don't believe so.
Exceptions need to be handled properly. Recursion does not.
The current implementation handles exceptions "properly" in the sense that if the once function exits with an exception then it is not marked as called and another thread (possibly one of those waiting) will call it. The suggestion is to optionally have the function marked as called even if an exception is thrown. I don't think this is worthwhile. Anthony -- Author of C++ Concurrency in Action | http://www.stdthread.co.uk/book/ just::thread C++0x thread library | http://www.stdthread.co.uk Just Software Solutions Ltd | http://www.justsoftwaresolutions.co.uk 15 Carrallack Mews, St Just, Cornwall, TR19 7UL, UK. Company No. 5478976

Anthony Williams wrote:
The current implementation handles exceptions "properly" in the sense that if the once function exits with an exception then it is not marked as called and another thread (possibly one of those waiting) will call it.
Does this mean the function will be called again? Isn't that disturbing for something named "call_once"?
The suggestion is to optionally have the function marked as called even if an exception is thrown. I don't think this is worthwhile.
The behavior I would expect instead is: if the function throws an exception, (an internal flag is set, and) other "call_once" calls will also throw an exception immediately, without calling the function again. Ideally (for me) the exception would be the one thrown by the function when it was called (i.e. the exception object would be caught and stored during the first "call_once" call, and then thrown again in other "call_once" calls) -- but I could live with other "call_once" calls throwing a system exception (something like "function_threw_exception") instead. I imagine some people will prefer the current behavior (and I guess it has to stay for compatibility's sake anyway), so maybe the two should be available somehow (new flag, policy or new function)? Regards, François

On Mon, Nov 23, 2009 at 9:45 AM, Francois Barel <frabar666@gmail.com> wrote:
Anthony Williams wrote:
The current implementation handles exceptions "properly" in the sense that if the once function exits with an exception then it is not marked as called and another thread (possibly one of those waiting) will call it.
Does this mean the function will be called again? Isn't that disturbing for something named "call_once"?
I think the spec of the original POSIX pthread_once was that it was a thread 'cancellation point', and they decided that the function would be considered 'uncalled' on cancellation. For C++ the analog would be exceptions. But I agree, it doesn't match the name. In our case, if the function threw, it must have been *called*. Maybe it didn't 'complete' (depending on your definition of complete) but it was called. At times in the the past, I've toyed with having two functions - call_once() and complete_once() (implemented by a common function internally). But then complete_once() would imply recursion also, so I decided to separate the concepts. IMO, the big advantage of re-calling on exception is that the same exception would likely be thrown in each thread. Which is hard to do otherwise. Tony

Gottlob Frege wrote:
But then complete_once() would imply recursion also, so I decided to separate the concepts.
If you look at local statics for guidance [6.7/4 in N3000]: "If the initialization exits by throwing an exception, the initialization is not complete, so it will be tried again the next time control enters the declaration." "If control re-enters the declaration recursively while the object is being initialized, the behavior is undefined."

On Mon, Nov 23, 2009 at 10:25 AM, Peter Dimov <pdimov@pdimov.com> wrote:
Gottlob Frege wrote:
But then complete_once() would imply recursion also, so I decided to separate the concepts.
If you look at local statics for guidance [6.7/4 in N3000]:
"If the initialization exits by throwing an exception, the initialization is not complete, so it will be tried again the next time control enters the declaration."
"If control re-enters the declaration recursively while the object is being initialized, the behavior is undefined."
I agree, particularly for local statics. I was just experimenting, and 'concurrently' there was an email here about it, so I tossed out my experiment.
No, it's the correct behavior. The function is not considered "called" unless it returns.
Unfortunately the function obviously was *called*. But I just think this means "call_once" is a slightly inaccurate name. It doesn't mean we should change the implementation. And "call_and_return_once" is a bit much. :-) Again, for me, it was just an experiment. The recursive check, in particular, I considered probably only useful when debugging (my version asserts in debug). In general, call_once and static locals are something of a hobby of mine, as sad as that is (ie I have no life). You might recall (as you were in some of the discussions) usenet posts in the past from me concerning: void f() { static_local<Foo> foo; ... } and void f() { static Once once; if (Once::Sentry sentry = once) { do_stuff_once(); etc(); } // or wait till done more_code(); } Or not recall, as there's lots of posts out there! Anyhow thanks for the opinions on exceptions and recursion. Confirmed my expectations. Tony

Francois Barel wrote:
Anthony Williams wrote:
The current implementation handles exceptions "properly" in the sense that if the once function exits with an exception then it is not marked as called and another thread (possibly one of those waiting) will call it.
Does this mean the function will be called again? Isn't that disturbing for something named "call_once"?
No, it's the correct behavior. The function is not considered "called" unless it returns. The use case for call_once is to perform initialization of a resource to be used. An exception means that the initialization failed.
The behavior I would expect instead is: if the function throws an exception, (an internal flag is set, and) other "call_once" calls will also throw an exception immediately, without calling the function again.
This is not how pthread_once and local statics work. Both specify that if the one-time initialization fails (is canceled in the first case, throws in the second), the initialization is retried the next time it's attempted. In the first case, specifically, it makes no sense to "rethrow" the cancelation exception when some other thread calls pthread_once.

Anthony Williams wrote:
"Peter Dimov" <pdimov@pdimov.com> writes:
Anthony Williams wrote:
Gottlob Frege <gottlobfrege@gmail.com> writes:
- if the exception handling and/or recursion handling are worthwhile options
I don't believe so.
Exceptions need to be handled properly. Recursion does not.
The current implementation handles exceptions "properly" in the sense that if the once function exits with an exception then it is not marked as called and another thread (possibly one of those waiting) will call it.
Yes, this is the correct behavior.
The suggestion is to optionally have the function marked as called even if an exception is thrown. I don't think this is worthwhile.
I agree.

On Mon, Nov 23, 2009 at 6:46 AM, Anthony Williams <anthony.ajw@gmail.com> wrote:
Gottlob Frege <gottlobfrege@gmail.com> writes:
Crikey that's a lot of code for a simple feature!
Yes, unfortunately. I've done it different ways in the past. I could probably give you a version that didn't build a linked list of nodes. But there are trade-offs.
I would just go for adding a "running" state to the flag value. If the flag is not "complete", set it to "running" using CAS. If we set it, then run, otherwise create an event, store it in the once_flag object (using CAS in case another waiter already stored an event), check the flag is still "running" and then wait for our event. When the event is triggered, loop again (to run the function if it threw last time).
Once the thread that runs the function has completed the function call it can then set the flag to "complete", and set the event if there is one. If the function throws then the flag can be set back to "not started" and the event set if there is one.
Anthony --
Yes, I've done it this way. You will have problems destroying the event. ie knowing who and when to destroy it. I don't think leaving the event handle leaking would be OK. You need to count it. It really isn't *too* hard, until you run into exceptions. Then you need to open the event and reset the once_flag. But now you need another event for the new attempt (you can't use the same event as you are not sure if all threads from the first attempt have even waited yet). So where do you store the event!? Using nodes solves that. You can also solve it by using what I call a 'branding gate' - the gist of it at least, from memory: as each thread enters, it increments a waiting count and/or the 'running' flag (lo/hi halves of an int, say). But on retry (after an exception) you don't just reset the running flag, you increment it - and 'running' becomes whether the flag is odd or even. This way every entry or wait is 'branded' with a unique value. You can then use the unique value to look up a named mutex (or named event). Windows takes care of ref counting for you. You still end up creating a global named handle, but it can be deferred until contention is detected. See also the discussion thread starting at http://www.sourceware.org/ml/pthreads-win32/2005/msg00016.html. This also ended in a node-based solution, although I think it used event handles on each node (iiuc). I never added my branding gate version to the discussion (hadn't figured it out at the time). A branding gate version does at least look somewhat simpler. Still not as straightforward as what you currently have. P.S. the other version you have (for posix) using the thread-local (based on the Google version) looks interesting as well. I think it would be nice to incorporate that, but avoid the global lock that is used. Tony

On Mon, Nov 23, 2009 at 9:46 AM, Gottlob Frege <gottlobfrege@gmail.com> wrote:
On Mon, Nov 23, 2009 at 6:46 AM, Anthony Williams <anthony.ajw@gmail.com> wrote:
Gottlob Frege <gottlobfrege@gmail.com> writes:
You can also solve it by using what I call a 'branding gate' - the gist of it at least, from memory: as each thread enters, it increments a waiting count and/or the 'running' flag (lo/hi halves of an int, say). But on retry (after an exception) you don't just reset the running flag, you increment it - and 'running' becomes whether the flag is odd or even. This way every entry or wait is 'branded' with a unique value. You can then use the unique value to look up a named mutex (or named event). Windows takes care of ref counting for you. You still end up creating a global named handle, but it can be deferred until contention is detected.
To be more clear: the 'running' half is more of an 'attempt' half. And that half is used as the unique value. So first thread comes in, finds 'running' to be 0, sets it to 1. All other threads increment waiting half. On exception, running becomes 2 (ie reset to 'retry'). Next thread in (maybe old waiter) sets running to 3. Waiting half can actually be reset to 0 at this point as it can be stored locally, at this point I think, although I'd have to look into it. And/or the global named event is ref counted and you don't really need to worry about the waiting count anymore. Something like that. Anyhow, it is only the 'running' half that is used as the unique-ifier for each attempt. Obviously if the whole branding gate was used, everyone would have their own event! Tony

Gottlob Frege <gottlobfrege@gmail.com> writes:
On Mon, Nov 23, 2009 at 6:46 AM, Anthony Williams <anthony.ajw@gmail.com> wrote:
I would just go for adding a "running" state to the flag value. If the flag is not "complete", set it to "running" using CAS. If we set it, then run, otherwise create an event, store it in the once_flag object (using CAS in case another waiter already stored an event), check the flag is still "running" and then wait for our event. When the event is triggered, loop again (to run the function if it threw last time).
Once the thread that runs the function has completed the function call it can then set the flag to "complete", and set the event if there is one. If the function throws then the flag can be set back to "not started" and the event set if there is one.
Yes, I've done it this way. You will have problems destroying the event. ie knowing who and when to destroy it. I don't think leaving the event handle leaking would be OK. You need to count it.
Yep.
It really isn't *too* hard, until you run into exceptions.
It's not that bad even then. Just use RAII to decrement the count on the way out.
Then you need to open the event and reset the once_flag. But now you need another event for the new attempt (you can't use the same event as you are not sure if all threads from the first attempt have even waited yet). So where do you store the event!? Using nodes solves that.
You don't need a new event. If the event is set it means "check the status". So thread enters call_once. Sees status is "running" and waits on the event. When the event is set it wakes and checks status. If status is complete, we're done. If status is running, wait for the same event again. If status is "not done", set it to running, reset the event and run the function. On completion, set status to "complete" and set the event. On exception, set status to "not done" and set the event. I'll code it up. Anthony -- Author of C++ Concurrency in Action | http://www.stdthread.co.uk/book/ just::thread C++0x thread library | http://www.stdthread.co.uk Just Software Solutions Ltd | http://www.justsoftwaresolutions.co.uk 15 Carrallack Mews, St Just, Cornwall, TR19 7UL, UK. Company No. 5478976

On Mon, Nov 23, 2009 at 12:26 PM, Anthony Williams <anthony.ajw@gmail.com> wrote:
You don't need a new event. If the event is set it means "check the status". So thread enters call_once. Sees status is "running" and waits on the event. When the event is set it wakes and checks status. If status is complete, we're done. If status is running, wait for the same event again. If status is "not done", set it to running, reset the event and run the function. On completion, set status to "complete" and set the event. On exception, set status to "not done" and set the event.
I'll code it up.
Anthony --
It is worth giving it a try, but we went down that road in pthreads-win32. I think the hard part is the 'reset the event' step. There were problems with whether the event should be manual or auto-reset, and with the 3rd thread in looping without waiting, etc. IIRC manual means you have a hard time knowing when to reset (ie did everyone or ANYONE wait yet - you could close too early), auto-reset meant that each 'waker' needed to wake the next one, etc. I can't remember what was really bad about chaining the waking of the waiters. Maybe it just felt a bit queasy. But I have a lot of faith in your work, so you may very well find something that we missed. Tony

Gottlob Frege <gottlobfrege@gmail.com> writes:
On Mon, Nov 23, 2009 at 12:26 PM, Anthony Williams <anthony.ajw@gmail.com> wrote:
You don't need a new event. If the event is set it means "check the status". So thread enters call_once. Sees status is "running" and waits on the event. When the event is set it wakes and checks status. If status is complete, we're done. If status is running, wait for the same event again. If status is "not done", set it to running, reset the event and run the function. On completion, set status to "complete" and set the event. On exception, set status to "not done" and set the event.
I'll code it up.
It is worth giving it a try, but we went down that road in pthreads-win32. I think the hard part is the 'reset the event' step. There were problems with whether the event should be manual or auto-reset, and with the 3rd thread in looping without waiting, etc. IIRC manual means you have a hard time knowing when to reset (ie did everyone or ANYONE wait yet - you could close too early), auto-reset meant that each 'waker' needed to wake the next one, etc. I can't remember what was really bad about chaining the waking of the waiters. Maybe it just felt a bit queasy.
I can see that there's potential for these problems. I also think I can see a way round them. I'll find out when I'm done coding.
But I have a lot of faith in your work, so you may very well find something that we missed.
Thanks for the vote of confidence. Anthony -- Author of C++ Concurrency in Action | http://www.stdthread.co.uk/book/ just::thread C++0x thread library | http://www.stdthread.co.uk Just Software Solutions Ltd | http://www.justsoftwaresolutions.co.uk 15 Carrallack Mews, St Just, Cornwall, TR19 7UL, UK. Company No. 5478976

Anthony Williams <anthony.ajw@gmail.com> writes:
Gottlob Frege <gottlobfrege@gmail.com> writes:
On Mon, Nov 23, 2009 at 12:26 PM, Anthony Williams <anthony.ajw@gmail.com> wrote:
You don't need a new event. If the event is set it means "check the status". So thread enters call_once. Sees status is "running" and waits on the event. When the event is set it wakes and checks status. If status is complete, we're done. If status is running, wait for the same event again. If status is "not done", set it to running, reset the event and run the function. On completion, set status to "complete" and set the event. On exception, set status to "not done" and set the event.
I'll code it up.
It is worth giving it a try, but we went down that road in pthreads-win32. I think the hard part is the 'reset the event' step. There were problems with whether the event should be manual or auto-reset, and with the 3rd thread in looping without waiting, etc. IIRC manual means you have a hard time knowing when to reset (ie did everyone or ANYONE wait yet - you could close too early), auto-reset meant that each 'waker' needed to wake the next one, etc. I can't remember what was really bad about chaining the waking of the waiters. Maybe it just felt a bit queasy.
I can see that there's potential for these problems. I also think I can see a way round them. I'll find out when I'm done coding.
I've checked in a new version on trunk https://svn.boost.org/svn/boost/trunk/boost/thread/win32/once.hpp This version only allocates an event if there is contention. The event (if there is one) is reset just before invoking the function in the one thread that gets to run it. It is set after the completion flag is set. The event is deallocated when the last thread that is using it exits call_once. If a thread exits because f() threw an exception then this is counted separately in order to avoid prematurely destroying the event. If f() throws every time it is called then the event will stick around until the flag is destroyed. It's not as tidy as I'd like, but I think the logic is sound. Anthony -- Author of C++ Concurrency in Action | http://www.stdthread.co.uk/book/ just::thread C++0x thread library | http://www.stdthread.co.uk Just Software Solutions Ltd | http://www.justsoftwaresolutions.co.uk 15 Carrallack Mews, St Just, Cornwall, TR19 7UL, UK. Company No. 5478976

I've checked in a new version on trunk
https://svn.boost.org/svn/boost/trunk/boost/thread/win32/once.hpp
This version only allocates an event if there is contention. The event (if there is one) is reset just before invoking the function in the one thread that gets to run it. It is set after the completion flag is set. The event is deallocated when the last thread that is using it exits call_once. If a thread exits because f() threw an exception then this is counted separately in order to avoid prematurely destroying the event. If f() throws every time it is called then the event will stick around until the flag is destroyed.
It's not as tidy as I'd like, but I think the logic is sound.
Probably, except you never actually wait on the event! :-) (or maybe I missed it). I'm assuming you mean to wait on it after the 'waiting' thread creates it if it doesn't exist: if(!counted) { BOOST_INTERLOCKED_INCREMENT(&flag.count); counted=true; status=::boost::detail::interlocked_read_acquire(&flag.status); if(status==function_complete_flag_value) { break; } event_handle=::boost::detail::interlocked_read_acquire(&flag.event_handle); if(!event_handle) { event_handle=::boost::detail::allocate_event_handle(flag.event_handle); continue; } ***** Wait(event_handle); ****** ? } Also, once_flag is no longer a POD (because of the destructor). Not sure if that is important. It is also now approx 4 * sizeof(long) instead of just sizeof(long or pointer). :-( Still looking at the overall algorithm... Tony

On Wed, Nov 25, 2009 at 12:59 AM, Gottlob Frege <gottlobfrege@gmail.com> wrote:
I've checked in a new version on trunk
https://svn.boost.org/svn/boost/trunk/boost/thread/win32/once.hpp
Probably, except you never actually wait on the event! :-) (or maybe I missed it).
I'm assuming you mean to wait on it after the 'waiting' thread creates it if it doesn't exist:
if(!counted) { BOOST_INTERLOCKED_INCREMENT(&flag.count); counted=true; status=::boost::detail::interlocked_read_acquire(&flag.status); if(status==function_complete_flag_value) { break; }
event_handle=::boost::detail::interlocked_read_acquire(&flag.event_handle); if(!event_handle) {
event_handle=::boost::detail::allocate_event_handle(flag.event_handle); continue; } ***** Wait(event_handle); ****** ? }
Or not quite there, because of the 'continue', but somewhere around there. (?)

Gottlob Frege <gottlobfrege@gmail.com> writes:
On Wed, Nov 25, 2009 at 12:59 AM, Gottlob Frege <gottlobfrege@gmail.com> wrote:
I've checked in a new version on trunk
https://svn.boost.org/svn/boost/trunk/boost/thread/win32/once.hpp
Probably, except you never actually wait on the event! :-) (or maybe I missed it).
I'm assuming you mean to wait on it after the 'waiting' thread creates it if it doesn't exist:
if(!counted) { ***** Wait(event_handle); ****** ? }
Or not quite there, because of the 'continue', but somewhere around there. (?)
Just outside the closing brace of the if(!counted) block. I've checked in the change. Anthony -- Author of C++ Concurrency in Action http://www.stdthread.co.uk/book/ just::thread C++0x thread library http://www.stdthread.co.uk Just Software Solutions Ltd http://www.justsoftwaresolutions.co.uk 15 Carrallack Mews, St Just, Cornwall, TR19 7UL, UK. Company No. 5478976

Gottlob Frege <gottlobfrege@gmail.com> writes:
I've checked in a new version on trunk
https://svn.boost.org/svn/boost/trunk/boost/thread/win32/once.hpp
It's not as tidy as I'd like, but I think the logic is sound.
Probably, except you never actually wait on the event! :-) (or maybe I missed it).
Oops! I thought I'd put it at the end of the loop.
Also, once_flag is no longer a POD (because of the destructor). Not sure if that is important. It is also now approx 4 * sizeof(long) instead of just sizeof(long or pointer). :-(
Lack of POD-ness is not important because it can still be statically initialized. The increased size is unfortunate. You can probably get away with packing the flags and counts into the same word at the expense of having to use a CAS loop to update them (for the counts to exceed even 16 bits would be highly unlikely). Anthony -- Author of C++ Concurrency in Action http://www.stdthread.co.uk/book/ just::thread C++0x thread library http://www.stdthread.co.uk Just Software Solutions Ltd http://www.justsoftwaresolutions.co.uk 15 Carrallack Mews, St Just, Cornwall, TR19 7UL, UK. Company No. 5478976
participants (5)
-
Anthony Williams
-
Francois Barel
-
Gottlob Frege
-
Peter Dimov
-
Vicente Botet Escriba