From: Gergely K. <ger...@ma...> - 2010-07-17 11:03:51
|
Hi Paul and Everyone, I am sorry that I have been silent in the last couple weeks, unfortunately I am very much underwater with other projects, and I had no time to clean up and resubmit our patches which solve this issue. We have already discussed approaches for this problem on this list. And also an implementation can be found in my horribly outdated patch ( http://xmlvm-reviews.appspot.com/18002, look in xmlvm.h and .m and in java_lang_Object.h and .m) although it seems that the critical portion, the NSRecursiveCondition is missing from there, I attached our current version of xmlvm.h and .m, which includes it. Unfortunately I won't be able to update the patch in the next few weeks, so if anyone would like to jump in, I would be very happy. The version in these patches are using a central, global registry of locks, to get around the problem that categories cannot contain fields. However, I think Panayotis found the associative references feature which allows us to "link" objects at runtime. You should be able to find this discussion in the list archives. Using associative references removes the bulk of the complexity of my solution, and it boils down to this: 1. Each java_lang_Object has a linked NSRecursiveCondition instance. This instance gets created on demand on the first access of [java_lang_Object lockImpl]; 2. monitorenter and monitorexit instructions generate [java_lang_Object lockImpl] and [unlockImpl] calls. 3. the wait* and notify* methods in java_lang_Object are simply using the NSRecursiveCondition instance that is linked with the Object instance. This solution is completely transparent. The compiler generates the monitorenter / exit instructions at the right places (with respect to exception handling ... etc.). We are using this solution with many locks and threads, wait / notify scenarios in our application and it works fine. It is possible that an NSRecursiveLock and an NSCondition instance together could remove the need of our own NSRecursiveCondition implementation, but I don't have the time now to think it over. On the simulator one could still use the central registry to store the lock objects, or provide a simplistic implementation for the associative references interface. Again, sorry that I did not have and still don't have the time to properly submit this change, and please if anyone has the time, feel free to take it over and (re)submit it. Best Regards, Gergely PS: Attachment got rejected by Sourceforge, here is the important part, the NSRecursiveCondition (the XMLVMWrapper can be safely ignored, and converted to the NSValue based solution.): @interface NSRecursiveCondition : NSCondition { NSMutableDictionary* waitingThreads; NSThread *lockingThread; int count; } - (id) init; - (void) dealloc; - (void) lock; - (void) unlock; - (void)wait; - (BOOL)waitUntilDate:(NSDate *)limit; - (void)signal; - (void)broadcast; - (BOOL)isFree; @end @implementation NSRecursiveCondition - (id) init { NSRecursiveCondition* c = [super init]; if (c != nil) { c->waitingThreads = [[NSMutableDictionary alloc] init]; c->count = 0; c->lockingThread = nil; } return c; } - (void) dealloc { [waitingThreads release]; if (lockingThread != nil) { [lockingThread release]; } [super dealloc]; } - (void) lock { NSThread *currentThread = [NSThread currentThread]; @synchronized (self) { if (currentThread == lockingThread) { ++ count; return; } } [super lock]; @synchronized (self) { if (lockingThread != nil) { @throw [[NSException alloc] initWithObject: @"BUG: lockingThread not nill after lock()"]; } ++ count; lockingThread = currentThread; } } - (void) unlock { NSThread *currentThread = [NSThread currentThread]; @synchronized (self) { if (currentThread != lockingThread) { return; } if (count > 1) { -- count; return; } [super unlock]; count = 0; lockingThread = nil; } } - (void)wait { NSThread *currentThread = [NSThread currentThread]; @synchronized (self) { if (currentThread != lockingThread) { java_lang_IllegalStateException* ex = [[java_lang_IllegalStateException alloc] init]; [ex __init_java_lang_IllegalStateException__]; @throw ex; } XMLVMKeyWrapper *key = [[XMLVMKeyWrapper alloc] initWithObject: currentThread]; [waitingThreads setObject: [NSNumber numberWithInteger: count] forKey: key]; [key release]; count = 0; lockingThread = nil; } [super wait]; @synchronized (self) { NSNumber* n = (NSNumber *) [waitingThreads objectForKey: currentThread]; count = [n intValue]; [waitingThreads removeObjectForKey: currentThread]; lockingThread = currentThread; } } - (BOOL)waitUntilDate:(NSDate *)limit { NSThread *currentThread = [NSThread currentThread]; @synchronized (self) { if (currentThread != lockingThread) { java_lang_IllegalStateException* ex = [[java_lang_IllegalStateException alloc] init]; [ex __init_java_lang_IllegalStateException__]; @throw ex; } XMLVMKeyWrapper *key = [[XMLVMKeyWrapper alloc] initWithObject: currentThread]; [waitingThreads setObject: [NSNumber numberWithInteger: count] forKey: key]; [key release]; count = 0; lockingThread = nil; } BOOL ret = [super waitUntilDate: limit]; @synchronized (self) { NSNumber* n = (NSNumber *) [waitingThreads objectForKey: currentThread]; count = [n intValue]; [waitingThreads removeObjectForKey: currentThread]; lockingThread = currentThread; } return ret; } - (void)signal { NSThread *currentThread = [NSThread currentThread]; @synchronized (self) { if (currentThread != lockingThread) { java_lang_IllegalStateException* ex = [[java_lang_IllegalStateException alloc] init]; [ex __init_java_lang_IllegalStateException__]; @throw ex; } [super signal]; } } - (void)broadcast { NSThread *currentThread = [NSThread currentThread]; @synchronized (self) { if (currentThread != lockingThread) { java_lang_IllegalStateException* ex = [[java_lang_IllegalStateException alloc] init]; [ex __init_java_lang_IllegalStateException__]; @throw ex; } [super broadcast]; } } - (BOOL) isFree { @synchronized (self) { if (lockingThread == nil && count == 0 && [waitingThreads count] == 0) { return TRUE; } return FALSE; } } @end 2010/7/17 Paul Poley <bay...@gm...> > This is a pretty critical change, so please bare with me on the length of > this e-mail. > I have attached some source code that may be the best explanation. (See > TestSync.java specifically) > > The major points are: > > 1. Changing XMLVM's synchronization > 2. Implementing wait(), wait(long), notify(), notifyAll() > 3. Considering alternatives to associative references to store member > variables in java_lang_Object > > > [...] -- Kis Gergely MattaKis Consulting Email: ger...@ma... Web: http://www.mattakis.com Phone: +36 70 408 1723 Fax: +36 27 998 622 |