From: Gustav M. <grd...@gm...> - 2008-08-25 15:29:11
Attachments:
autofinalize.patch
|
I'm implementing a HOC/Cocoa frontend for the Yi editor, and so far it works really well. I was puzzled about messages concerning autoreleasing without a pool in place. After some digging I have concluded that the problem was that there was no autorelease pool in place while finalizers were executed. I tried wrapping the finalizers in withAutoreleasePool which removed the messages, but resulted in Yi crashing after some time when an autoreleasepool is deallocated twice. I can reproduce the error, but not predictably. I then tried wrapping only the actual calls to release/dealloc in the finalizer, and with quite some testing I have not been able to reproduce the above failure. I do not understand why this works, but this _is_ in the "Arcane Magic" section of HOC, so I guess I should not be too surprised. =) Is this the right way to fix this problem? See attached patch for changes. !g |
From: Daniel R. <da...@ph...> - 2008-08-25 16:22:23
|
On Aug 25, 2008, at 8:29 AM, Gustav Munkby wrote: > I'm implementing a HOC/Cocoa frontend for > the Yi editor, and so far it works really well. > > I was puzzled about messages concerning > autoreleasing without a pool in place. After > some digging I have concluded that the > problem was that there was no autorelease > pool in place while finalizers were executed. Hmm, there should always be an autorelease pool in place. If you're not using one of apples built in event loops, you should allocate/ deallocate an autorelease pool in your main method. That being said, not having finalizer autoreleased objects actually release until the program exits is probably unacceptable. hmm, are these finalizers being run after the main method exits? If so, it's actually safe to ignore those warnings. > I tried wrapping the finalizers in > withAutoreleasePool which removed the > messages, but resulted in Yi crashing after > some time when an autoreleasepool is > deallocated twice. I can reproduce the > error, but not predictably. I'd really like to understand this error. It would go a long way toward understanding if this is the "right way." As far as I can tell, that should work. > I then tried wrapping only the actual calls > to release/dealloc in the finalizer, and with > quite some testing I have not been able > to reproduce the above failure. While I don't understand why wrapping the finalizers didn't work, and that bugs me, this is more or less how you are supposed to use autorelease pools. > I do not understand why this works, but > this _is_ in the "Arcane Magic" section > of HOC, so I guess I should not be too > surprised. =) Autorelease pools work by telling the runtime "I no longer need this object, but I'm pretty sure someone else is going to want it, so I'll just put a message in the pool, and go ahead an deallocate the object when the pool is released." It's essentially a delayed release. It's good practice to sprinkle your code with new autorelease pools whenever you're allocating then deallocating a large number of objects so that stuff is actually released and the memory is freed when you exit that section. Otherwise you have to rely on the top level autorelease which is probably allocated in (or close too) your main method. > Is this the right way to fix this problem? Arguably, the right way is to side step the issue entirely and use garbage collected interfaces. Otherwise yes, (other than not understanding why wrapping the entire finalizer didn't work). Also, I don't believe dealloc is supposed to require an autorelease pool. Though someone could always override dealloc and call autorelease and objective-c expects there to always be an autorelease pool available. -- Daniel |
From: Gustav M. <grd...@gm...> - 2008-08-26 12:36:22
|
On Mon, Aug 25, 2008 at 6:22 PM, Daniel Rogers <da...@ph...> wrote: > > On Aug 25, 2008, at 8:29 AM, Gustav Munkby wrote: > >> I'm implementing a HOC/Cocoa frontend for >> the Yi editor, and so far it works really well. >> >> I was puzzled about messages concerning >> autoreleasing without a pool in place. After >> some digging I have concluded that the >> problem was that there was no autorelease >> pool in place while finalizers were executed. > > Hmm, there should always be an autorelease pool in place. If you're not > using one of apples built in event loops, you should allocate/deallocate an > autorelease pool in your main method. That being said, not having finalizer > autoreleased objects actually release until the program exits is probably > unacceptable. For the main application there is one main autorelease pool available during the scope of the whole program, and then the run-loop creates a new pool every iteration (according to the documentation). > hmm, are these finalizers being run after the main method exits? If so, > it's actually safe to ignore those warnings. The issue is that the finalizers are being executed in their own thread, and Cocoa needs autorelease pools to be established in each thread. The finalizers are not executed after the application finishes, but rather during Haskell garbage collection. >> I tried wrapping the finalizers in >> withAutoreleasePool which removed the >> messages, but resulted in Yi crashing after >> some time when an autoreleasepool is >> deallocated twice. I can reproduce the >> error, but not predictably. > > I'd really like to understand this error. It would go a long way toward > understanding if this is the "right way." As far as I can tell, that should > work. Yes, that seems reasonable. However, I cannot say that I exactly understand the interaction between Haskell and Objective-C in terms of memory management. >> I then tried wrapping only the actual calls >> to release/dealloc in the finalizer, and with >> quite some testing I have not been able >> to reproduce the above failure. > > While I don't understand why wrapping the finalizers didn't work, and that > bugs me, this is more or less how you are supposed to use autorelease pools. > >> I do not understand why this works, but >> this _is_ in the "Arcane Magic" section >> of HOC, so I guess I should not be too >> surprised. =) > > Autorelease pools work by telling the runtime "I no longer need this object, > but I'm pretty sure someone else is going to want it, so I'll just put a > message in the pool, and go ahead an deallocate the object when the pool is > released." It's essentially a delayed release. It's good practice to > sprinkle your code with new autorelease pools whenever you're allocating > then deallocating a large number of objects so that stuff is actually > released and the memory is freed when you exit that section. Otherwise you > have to rely on the top level autorelease which is probably allocated in (or > close too) your main method. This is a good description of autorelease pools. The one thing left out that is relevant for this discussion is that there is a stack of autorelease pools per thread, and outside the main thread you are not only encouraged, but even required to set up an autorelease pool. >> Is this the right way to fix this problem? > > Arguably, the right way is to side step the issue entirely and use garbage > collected interfaces. Otherwise yes, (other than not understanding why > wrapping the entire finalizer didn't work). That is a completely different can-of-worms, which would indeed solve this issue. However, having both Haskell and Objective-C run their own garbage collectors would supposedly require even more "Arcane Magic". > Also, I don't believe dealloc is supposed to require an autorelease pool. > Though someone could always override dealloc and call autorelease and > objective-c expects there to always be an autorelease pool available. I'm quite sure that the normal release message is also not using any pools. >From the looks of the messages produced by Yi, an NSView autoreleases its child views upon receiving a dealloc message. !g |
From: Daniel R. <da...@ph...> - 2008-08-26 14:45:58
|
On Aug 26, 2008, at 5:36 AM, Gustav Munkby wrote: > The issue is that the finalizers are being executed in their own > thread, > and Cocoa needs autorelease pools to be established in each thread. > The finalizers are not executed after the application finishes, but > rather > during Haskell garbage collection. Ah. I hadn't realized there was a separate system thread for garbage collection (I thought it was only a lightweight thread). That's what I get for being new to HOC... > That is a completely different can-of-worms, which would indeed > solve this > issue. However, having both Haskell and Objective-C run their own > garbage > collectors would supposedly require even more "Arcane Magic". Indeed. Though from what I've been able to gather, it should be possible. I went ahead and applied your patch. Even if there was a way to install an autorelease pool in the garbage collector thread, it's probably not what I want, since the garbage collector thread never exits. -- Daniel |
From: Wolfgang T. <wol...@gm...> - 2008-09-07 21:31:44
|
First of all, it's great to see this list really coming to life :-). On Aug 25, 2008, at 5:29 PM, Gustav Munkby wrote: > I do not understand why this works, but > this _is_ in the "Arcane Magic" section > of HOC, so I guess I should not be too > surprised. =) > I think the original problem is that there was not necessarily an autorelease pool available for use by an object's dealloc method, so wrapping the calls to dealloc and release should be the right thing to do. > Is this the right way to fix this problem? I think it's *almost* the right way. More importantly, in this case, I think that the autorelease pools have to be created and destroyed in HOC_cbits/MemoryManagement.m rather than on the Haskell side. I still have to double-check this, but I seem to remember that GHC finalisers are run as lightweight threads by GHC, which means that we get no guarantee on which of the GHC runtime system's worker OS threads gets to execute each timeslice of the finaliser; it is therefore theoretically possible that the pool gets installed in one OS thread, but the actual dealloc executes in another, and the pool dealloc in yet another OS thread (which might have a *different* pool in place, causing lots of confusion). Not likely, but I am kind of afraid of it. > I tried wrapping the finalizers in > withAutoreleasePool which removed the > messages, but resulted in Yi crashing after > some time when an autoreleasepool is > deallocated twice. I can reproduce the > error, but not predictably. I think your attempt of wrapping the entire finaliser might have caused the time window for this problem to be longer, and therefore just made the crashes more likely than if you just wrap the actual dealloc. So we'll probably have to do it the hard way (we can't use *real* objective C if we want to be able to use GHCi, so we have to send all the messages "by hand" from C). Cheers, Wolfgang |
From: Gustav M. <grd...@gm...> - 2008-09-08 09:48:06
Attachments:
autofinalize2.patch
|
>> Is this the right way to fix this problem? > > I think it's *almost* the right way. > More importantly, in this case, I think that the autorelease pools > have to be created and destroyed in HOC_cbits/MemoryManagement.m > rather than on the Haskell side. > I still have to double-check this, but I seem to remember that GHC > finalisers are run as lightweight threads by GHC, which means that we > get no guarantee on which of the GHC runtime system's worker OS > threads gets to execute each timeslice of the finaliser; it is > therefore theoretically possible that the pool gets installed in one > OS thread, but the actual dealloc executes in another, and the pool > dealloc in yet another OS thread (which might have a *different* pool > in place, causing lots of confusion). Not likely, but I am kind of > afraid of it. If this is the case, it would explain the behavior that I'm seeing, but with my limited understanding of how the GHC internals work, there could be a lot of other plausible explanations as well. =) >> I tried wrapping the finalizers in >> withAutoreleasePool which removed the >> messages, but resulted in Yi crashing after >> some time when an autoreleasepool is >> deallocated twice. I can reproduce the >> error, but not predictably. > > I think your attempt of wrapping the entire finaliser might have > caused the time window for this problem to be longer, and therefore > just made the crashes more likely than if you just wrap the actual > dealloc. So we'll probably have to do it the hard way (we can't use > *real* objective C if we want to be able to use GHCi, so we have to > send all the messages "by hand" from C). Actually, that does not seem to be a major problem, since we can just reuse the functions already declared in MemoryManagement.h. See the attached patch for a first go at this. !g |
From: Wolfgang T. <wol...@gm...> - 2008-09-08 22:08:27
|
On Sep 8, 2008, at 11:48 AM, Gustav Munkby wrote: > Actually, that does not seem to be a major problem, since we can > just reuse the functions already declared in MemoryManagement.h. > See the attached patch for a first go at this. Good. I had forgotten about them. I have verified that finalisers are run in lightweight ("unbound") threads, so I am now pretty convinced that my theory is right. I like your patch. Do you want commit access? Cheers, Wolfgang |
From: Gustav M. <grd...@gm...> - 2008-09-09 05:09:58
|
> I have verified that finalisers are run in lightweight ("unbound") threads, > so I am now pretty convinced that my theory is right. Excellent. > I like your patch. Do you want commit access? Sure, that would be most convenient for the future. !g |