February 11, 2007
torhu wrote:
> Bill Baxter wrote:
> 
>> Is there any reason not to make the format item's index also optional? So that
>>     Formatter("{} {} {}", a, b, c);
>> can be used?  I mean making it more like %s?
> 
> 
> I like it. :)

it's in
February 11, 2007
kris wrote:
> Charles D Hixson wrote:
>> I'm not sure whether you were quoting the documentation, or reporting your understanding.  If you were quoting the documentation, I think it needs editing.
> 
>  From the doc: "Please note that the class itself is stateful, and therefore a single instance is not shareable across multiple threads."
> 
> Thus, it is considered unwise to share a single instance of Sprint across multiple threads. However, multiple threads /can/ share a single instance if they follow this pattern:
> 
> synchronized (GlobalSprint)
>               GlobalSprint ("do my formatting", with, these, args);
> 
> This is a fairly standard mechanism in D for sharing resources across threads, and it's what I had referred to.

For what it's worth, Tango also provides thread-local storage, and while it isn't ideal from a semantic standpoint (a bona-fide 'local' storage class would be much nicer), it's an option worth considering.  It could be used like so:

    auto TLSPrint = new ThreadLocal!(Sprint);
    // do this in each thread
    TLSPrint.val = new Sprint;

    // use the local Sprint instance
    TLSPrint.val()( "do my formatting", with, these, args );

Accessing a TLS value tyically amounts to a few pointer dereferences so it's far less costly than acquiring a mutex, which may or may not be important to you.

As a side-note... Tango currently provides 64 TLS "slots".  This amount is fixed mostly to keep performance as high as possible, but it could easily be increased if it turns out not to be enough.


Sean
February 11, 2007
Sean Kelly wrote:

> kris wrote:
>> Charles D Hixson wrote:
>>> I'm not sure whether you were quoting the documentation, or reporting your understanding.  If you were quoting the documentation, I think it needs editing.
>> 
>>  From the doc: "Please note that the class itself is stateful, and
>> therefore a single instance is not shareable across multiple threads."
>> 
>> Thus, it is considered unwise to share a single instance of Sprint across multiple threads. However, multiple threads /can/ share a single instance if they follow this pattern:
>> 
>> synchronized (GlobalSprint)
>>               GlobalSprint ("do my formatting", with, these, args);
>> 
>> This is a fairly standard mechanism in D for sharing resources across threads, and it's what I had referred to.
> 
> For what it's worth, Tango also provides thread-local storage, and while it isn't ideal from a semantic standpoint (a bona-fide 'local' storage class would be much nicer), it's an option worth considering.  It could be used like so:
> 
>      auto TLSPrint = new ThreadLocal!(Sprint);
>      // do this in each thread
>      TLSPrint.val = new Sprint;
> 
>      // use the local Sprint instance
>      TLSPrint.val()( "do my formatting", with, these, args );
> 
> Accessing a TLS value tyically amounts to a few pointer dereferences so it's far less costly than acquiring a mutex, which may or may not be important to you.
> 
> As a side-note... Tango currently provides 64 TLS "slots".  This amount is fixed mostly to keep performance as high as possible, but it could easily be increased if it turns out not to be enough.
> 
> 
> Sean

What happens when you run out of slots?

Would it be possible to have a dynamic amount of slots (doubling whenever
there is to few)?
February 11, 2007
Johan Granberg wrote:
> Sean Kelly wrote:
> 
>> kris wrote:
>>> Charles D Hixson wrote:
>>>> I'm not sure whether you were quoting the documentation, or reporting
>>>> your understanding.  If you were quoting the documentation, I think it
>>>> needs editing.
>>>  From the doc: "Please note that the class itself is stateful, and
>>> therefore a single instance is not shareable across multiple threads."
>>>
>>> Thus, it is considered unwise to share a single instance of Sprint
>>> across multiple threads. However, multiple threads /can/ share a single
>>> instance if they follow this pattern:
>>>
>>> synchronized (GlobalSprint)
>>>               GlobalSprint ("do my formatting", with, these, args);
>>>
>>> This is a fairly standard mechanism in D for sharing resources across
>>> threads, and it's what I had referred to.
>> For what it's worth, Tango also provides thread-local storage, and while
>> it isn't ideal from a semantic standpoint (a bona-fide 'local' storage
>> class would be much nicer), it's an option worth considering.  It could
>> be used like so:
>>
>>      auto TLSPrint = new ThreadLocal!(Sprint);
>>      // do this in each thread
>>      TLSPrint.val = new Sprint;
>>
>>      // use the local Sprint instance
>>      TLSPrint.val()( "do my formatting", with, these, args );
>>
>> Accessing a TLS value tyically amounts to a few pointer dereferences so
>> it's far less costly than acquiring a mutex, which may or may not be
>> important to you.
>>
>> As a side-note... Tango currently provides 64 TLS "slots".  This amount
>> is fixed mostly to keep performance as high as possible, but it could
>> easily be increased if it turns out not to be enough.
> 
> What happens when you run out of slots?

An exception is thrown.  Please note that 64 slots doesn't mean each thread occupies a separate slot however, but rather than there is storage reserved for 64 different thread-local variables.  For a typical application, I think this is more than sufficient.

> Would it be possible to have a dynamic amount of slots (doubling whenever
> there is to few)?

Not without involving a mutex.  When a TLS slot is deleted, the algorithm visits each thread and nulls out the appropriate slot.  With a fixed-size array this just works, but if the array can be resized then there's a chance it may be resized at exactly the wrong moment and you end up with a segfault.  The ideal scenario would be to use the operating system's built-in thread-local storage mechanism, but there is no efficient way to make the GC aware of these locations (at least not without some clever hacking based on low-level knowledge of how each OS actually stored this information, and it is different for every OS). The current approach was chosen simply because it was the most general, efficient, and reliable.


Sean
February 11, 2007
torhu wrote:
> Frits van Bommel wrote:
>> On a related note, one of the things that bothers /me/ is that no flush is performed at the end of the program. That causes some or all of the output to be missing if you don't explicitly flush after the last output.
>> I'd suggest adding the following to tango.io.Console:
>> ---
>> static ~this ()
>> {
>>          Cout.flush();
>>          Cerr.flush();
>> }
>> ---
>>
>> That would fix it, I think.
> 
> I considered this, but I'll try to explain why I didn't suggest it. Basically, it hides that fact that you have forgotten to flush.
> 
> Pretend that this example is a big piece of code, that sometimes crashes.  To debug, you print some numbers, and where the numbers stop should be were the program crashes.
> 
> ---
> import tango.io.Stdout;
> import tango.stdc.stdlib;
> import tango.stdc.time;
> 
> void main()
> {
>     // some code here
> 
>     Stdout("1");
> 
>     // silly example of 'sometimes crash'
>     if (time(null) & 1)
>         free(cast(void*)1);
>         Stdout("2");   
> 
>     // more code here, etc
> 
>     Stdout("3");
> }
> ---
> 
> The problem is that with auto flush at program end, it will print '123' when it doesn't crash, and nothing at all when it does crash.  So the flushing happens at the 'wrong' time, unless you're aware of the inner workings of Tango.  A crash, exit by calling exit() or abort(), or an uncaught exception will cause the auto flush not to happen.  Which seems a bit inconsistent.
> 
> How realistic or valid this concern is, I'm not sure.  But it's at least it made me not suggest auto flushing.
> 
> By the way, is there a particular reason why Cerr is buffered?

But forgetting to flush is a bug in user code *because* the system does not flush.  If the system flushed, then the user code is correct.

Put another way, this design decision (to not flush) helps programs that crash (the output is more deterministic), but it hurts programs that do not crash.  If a program would have been correct (on a system that flushes stdout), it now isn't because this system doesn't.

Kevin
1 2 3 4 5
Next ›   Last »