[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