Download Reference Manual
The Developer's Library for D
About Wiki Forums Source Search Contact

Ticket #1046 (closed defect: invalid)

Opened 17 years ago

Last modified 17 years ago

Weakref does not work with Tango due to possible bugs in Tango's GC

Reported by: JarrettBillingsley Assigned to: sean
Priority: normal Milestone: 0.99.7
Component: Core Functionality Version: 0.99.5 Jascha
Keywords: triage Cc:

Description

scrapple.weakref doesn't seem to work right in tango.

The following file is an all-in-one test that includes the definition of weakref:

/*==========================================================================
 * weakref.d
 *    Written in the D Programming Language (http://www.digitalmars.com/d)
 */
/***************************************************************************
 * Creates a weak reference to a class instance.
 *
 * A weak reference lets you hold onto a pointer to an object without 
 * preventing the garbage collector from collecting it.
 * If the garbage collector collects the object then the weak pointer will 
 * become 'null'.  Thus one should always check a weak pointer for null
 * before doing anything that depends upon it having a value.
 *
 * Tested with:
 *    DMD 1.025 / Phobos 1.025
 *    DMD 1.025 / Tango 0.99.4
 * 
 * Usage example:
---
 class Something {}

 auto a = new Something();
 auto wa = new WeakRef!(Something)(a);
 std.gc.fullCollect();
    
 // Reference 'a' prevents collection so wa.ptr is non-null
 assert(wa.ptr is a);

 delete a;
 
 // 'a' is gone now, so wa.ptr magically becomes null
 assert(wa.ptr is null);
---
 *    
 *
 * Author:  William V. Baxter III
 * Contributors: 
 * Date: 21 Jan 2008
 * Copyright: (C) 2008  William Baxter
 * License: Public Domain where allowed by law, ZLIB/PNG otherwise.
 */
//===========================================================================

module weakref;
version=Tango;
version=UnitTest;
version(Tango) {
    private {
        alias void delegate(Object) DisposeEvt;
        extern (C) void  rt_attachDisposeEvent( Object obj, DisposeEvt evt );
        extern (C) void  rt_detachDisposeEvent( Object obj, DisposeEvt evt );
    }
}

class WeakRef(T : Object) {
private:
    size_t cast_ptr_;
    void unhook(Object o) {
        if (cast(size_t)cast(void*)o == cast_ptr_) {
            version(Tango) {
                rt_detachDisposeEvent(o, &unhook);
            } else {
                o.notifyUnRegister(&unhook);
            }
            cast_ptr_ = 0;
        }
    }
public:

    this(T tptr) {
        cast_ptr_ = cast(size_t)cast(void*)tptr;
        if (cast_ptr_ == 0)
            return;
        version(Tango) {
            rt_attachDisposeEvent(tptr, &unhook);
        } else {
            tptr.notifyRegister(&unhook);
        }
    }
    ~this() {
        T p = ptr();
        if (p) {
            version(Tango) {
                rt_detachDisposeEvent(p, &unhook);
            } else {
                p.notifyUnRegister(&unhook);
            }
        }
    }
    T ptr() {
        return cast(T)cast(void*)cast_ptr_;
    }
    WeakRef dup() {
        return new WeakRef(ptr());
    }
}


version(UnitTest) {

version(Tango) {
    import tango.core.Memory;
    alias GC.collect collect;
} else {
    static import std.gc;
    alias std.gc.fullCollect collect;
}

unittest {
    class Something {
        int value;
        this(int v) { value = v; }
        ~this() { value = -1; }
    }

    WeakRef!(Something) wa;

    auto a = new Something(1);
    wa = new WeakRef!(Something)(a);
    assert(a is wa.ptr);

    collect();
    assert(a is wa.ptr);

    delete a;

    // a now gone so should be collected
    collect();
    
    assert(wa.ptr is null);
}

unittest{
    class Something {
        int value;
        this(int v) { value = v; }
        ~this() { value = -1; }
    }

    WeakRef!(Something) makeWa(){
        Something a = new Something(1);
        auto wa = new WeakRef!(Something)(a);
        return wa;
    }

    WeakRef!(Something) wa;
    wa = makeWa;
    assert(wa.ptr !is null);

    collect();

    assert(wa.ptr is null);
}
}

void main(){
}

Compile it with -unittest and run it.

With DMDWin, the last assertion fails because for some reason, the object pointed to by the weak reference is never collected.

With GDC on Linux, there is instead an error in GC.collect(). The glibc free() function fails possibly due to a double-free.

Has not been tested with DMDLinux or with Phobos.

Change History

05/24/08 18:46:46 changed by larsivi

  • keywords set to triage.

06/03/08 15:34:21 changed by sean

  • status changed from new to closed.
  • resolution set to invalid.

Invalid unit test. Try this code:

!#d
import tango.core.Memory;
import tango.stdc.stdio;

void main()
{
    class C { ~this() { printf( "bye bye\n" ); } }
    auto a = new WeakRef!(C)( new C );
    auto b = new C;
    GC.collect();
    printf( "woot\n" );
}

You'll see it print:

bye bye woot bye bye

On Win32 using Tango. For testing object collection, you have to be sure that all registers which may refer to that object have been reset. The easiest way to do this is to construct two objects.

06/08/08 18:42:55 changed by JarrettBillingsley

Aha. Something bizzare. Your code works as long as -g is not passed to DMD.

If -g is passed, I get the following.

woot
bye bye
bye bye

That is, the object is not collected until program exit, when it would be collected anyway.

In fact, if I add:

if(wa.ptr !is null)
    throw new Exception("Not null!");

At the end of main, it succeeds in either debug or release mode as long as -g is not passed, and fails in both if it is.

This doesn't seem like anything that you can fix -- it's not like using -g causes a version to be set. I have no idea, though, why adding debug info would cause code to act differently. One of those mysterious, show-stopping DMD bugs that will never be fixed, I reckon.

Still -- the issue on GDC remains. Have you tested it?

06/09/08 15:17:43 changed by sean

I haven't tested GDC, but the same GC is used by both DMD and GDC so I'm skeptical that it's a GC problem. The -g bug sounds like a potential codegen issue though. Does it work with Phobos?

06/11/08 23:02:22 changed by JarrettBillingsley

GDC+Phobos doesn't give an error with free(), but WeakRef? just does not work. No matter what flags I pass/don't pass. It always fails on the assertions and your code gives "woot bye bye bye bye".

Sigh. Now nothing's conclusive.