[Issue 16232] std.experimental.logger.core.sharedLog isn't thread-safe
    via Digitalmars-d-bugs 
    digitalmars-d-bugs at puremagic.com
       
    Fri Jul 14 09:02:46 PDT 2017
    
    
  
https://issues.dlang.org/show_bug.cgi?id=16232
ag0aep6g at gmail.com changed:
           What    |Removed                     |Added
----------------------------------------------------------------------------
             Status|RESOLVED                    |REOPENED
         Resolution|FIXED                       |---
--- Comment #6 from ag0aep6g at gmail.com ---
(In reply to Robert Schadek from comment #3)
> I will add a comment to make that clear
Reopening. I think a comment isn't enough. sharedLog is marked as @safe, but it
effectively casts shared away, which isn't safe. It can lead to memory
corruption when shared data can be accessed as unshared.
I see two ways out:
1) Make sharedLog @system.
2) Return a shared Logger.
Lengthy example of @safe violation with custom Logger:
----
import std.experimental.logger.core: Logger, LogLevel, sharedLog;
class MyLogger : Logger
{
    ulong* p;
    this() @safe
    {
        super(LogLevel.all);
        /* Allocate a ulong that disrespects cache line boundaries (64 bytes),
        so that it won't be loaded/stored atomically. */
        align(64) static struct S
        {
            align(1):
            ubyte[60] off;
            ulong x = 0;
        }
        auto s = new S;
        this.p = &s.x;
        assert((cast(size_t) p) % 64 == 60);
        assert((cast(size_t) p) % s.x.sizeof == 4);
    }
    override void writeLogMsg(ref LogEntry payload) @safe { assert(false); }
        /* never called */
}
MyLogger sharedMyLogger() @safe
{
    Logger logger = sharedLog();
    return cast(MyLogger) logger;
        /* This is a simple downcast. Not casting away shared. */
}
enum n = 1_000_000;
/* Toggle *p between 0 and ulong.max (n times). */
void write(ulong* p) @safe
{
    foreach (i; 0 .. n) *p = ~*p; /* non-atomic load and store */
}
/* Assert that *p is either 0 or ulong.max (n times). */
void read(ulong* p) @safe
{
    import std.conv: to;
    foreach (i; 0 .. n)
    {
        ulong val = *p; /* non-atomic load */
        assert(val == 0 || val == ulong.max, val.to!string(16)); /* fails */
    }
}
void main()
{
    sharedLog = new MyLogger;
    /* Read and write concurrently. `read` will see a partially written value.
    I.e., memory corruption. */
    import core.thread: Thread;
    new Thread(() @safe { write(sharedMyLogger.p); }).start();
    read(sharedMyLogger.p);
}
----
--
    
    
More information about the Digitalmars-d-bugs
mailing list