From: <cod...@go...> - 2009-01-10 01:15:29
|
Author: jam...@us... Date: Fri Jan 9 16:57:06 2009 New Revision: 380 Modified: / (props changed) branches/objc2/hoc/HOC/HOC/ExportClass.hs branches/objc2/hoc/HOC/HOC/ID.hs branches/objc2/hoc/HOC/HOC/NewClass.hs branches/objc2/hoc/HOC_cbits/MemoryManagement.h branches/objc2/hoc/HOC_cbits/MemoryManagement.m Log: (objc2 branch) Rewrote retain/release to call superclass's implementations. Modified: branches/objc2/hoc/HOC/HOC/ExportClass.hs ============================================================================== --- branches/objc2/hoc/HOC/HOC/ExportClass.hs (original) +++ branches/objc2/hoc/HOC/HOC/ExportClass.hs Fri Jan 9 16:57:06 2009 @@ -130,8 +130,8 @@ ivars <- makeDefaultIvarList imethods <- makeMethodList (nIMethods+3) cmethods <- makeMethodList nCMethods - setHaskellRetainMethod imethods 0 - setHaskellReleaseMethod imethods 1 + setHaskellRetainMethod imethods 0 super + setHaskellReleaseMethod imethods 1 super setHaskellDataMethod imethods 2 super ( Just ($(typedInitIvars) >>= return . toDyn) ) Modified: branches/objc2/hoc/HOC/HOC/ID.hs ============================================================================== --- branches/objc2/hoc/HOC/HOC/ID.hs (original) +++ branches/objc2/hoc/HOC/HOC/ID.hs Fri Jan 9 16:57:06 2009 @@ -8,7 +8,7 @@ import Control.Concurrent.MVar import Control.Exception(evaluate,assert) -import Control.Monad(when) +import Control.Monad(when, join) import System.IO.Unsafe(unsafePerformIO) import System.Mem.Weak import Foreign.Ptr @@ -21,6 +21,12 @@ data ID a = ID HSO | Nil +dPutStrLn = if {--} False --} True + then putStrLn + else const $ return () + +dPutWords = dPutStrLn . unwords + nil = Nil castObject (ID a) = ID a @@ -59,6 +65,13 @@ objectMapLock = unsafePerformIO $ newMVar () {-# NOINLINE objectMapLock #-} +withObjectMapLock taker action = do + dPutWords [">", "withObjectMapLock", taker] + res <- withMVar objectMapLock $ \_ -> action + dPutWords ["<", "withObjectMapLock", taker] + return res + + -- given a pointer to an ObjCObject, return a stablePtr to a Weak reference to -- a HSO foreign import ccall unsafe "ObjectMap.h getHaskellPart" @@ -91,13 +104,21 @@ getRetainedHaskellPart :: Ptr ObjCObject -> IO (StablePtr HSO) foreign import ccall unsafe "RetainedHaskellPart.h setRetainedHaskellPart" setRetainedHaskellPart :: Ptr ObjCObject -> StablePtr HSO -> IO () - -foreign import ccall unsafe "NSObjectReferenceCount.h NSIncrementExtraRefCount" - nsIncrementExtraRefCount :: Ptr ObjCObject -> IO () -foreign import ccall unsafe "NSObjectReferenceCount.h NSDecrementExtraRefCountWasZero" - nsDecrementExtraRefCountWasZero :: Ptr ObjCObject -> IO CChar{-BOOL-} -foreign import ccall unsafe "NSObjectReferenceCount.h NSExtraRefCount" - nsExtraRefCount :: Ptr ObjCObject -> IO CUInt +replaceRetainedHaskellPart :: Ptr ObjCObject -> StablePtr HSO -> IO () +replaceRetainedHaskellPart self newHSO = do + dPutWords ["replaceRetainedHaskellPart", show self, show (castStablePtrToPtr newHSO)] + oldHSO <- getRetainedHaskellPart self + when (oldHSO /= newHSO) $ do + when (castStablePtrToPtr oldHSO /= nullPtr) $ do + freeStablePtr oldHSO + setRetainedHaskellPart self newHSO + +foreign import ccall "MemoryManagement.h retainSuper" + retainSuper :: Ptr ObjCObject -> Ptr ObjCObject -> IO () +foreign import ccall "MemoryManagement.h releaseSuper" + releaseSuper :: Ptr ObjCObject -> Ptr ObjCObject -> IO () +foreign import ccall unsafe "MemoryManagement.h retainCount" + retainCount :: Ptr ObjCObject -> IO CUInt -- Since finalizers are executed in arbitrary threads, we must -- ensure that we establish an autoreleasepool for the duration @@ -147,11 +168,13 @@ importImmortal = importArgument' True --- this is where the mogic happens. +-- this is where the magic happens. importArgument' immortal p | p == nullPtr = return Nil - -- objectMapLock is a global, thanks to unsafePerformIO - | otherwise = withMVar objectMapLock $ \_ -> do + -- do what needs to be done in the lock, return what + -- needs to be done outside the lock (specifically, + -- the retain needs to be done outside the lock). + | otherwise = join $ withObjectMapLock "importArgument'" $ do sptr <- getHaskellPart p mbHaskellObj <- if castStablePtrToPtr sptr /= nullPtr @@ -162,39 +185,31 @@ return Nothing case mbHaskellObj of -- if the HSO already exists, we're done! - Just haskellObj -> return $ ID haskellObj + Just haskellObj -> return $ return $ ID haskellObj -- notice that the finalizer definition requires new_sptr Nothing -> mdo {- it's much more pratical than fixM -} haskellData <- makeNewHaskellData p + dPutWords ["got haskell data", show haskellData] let haskellObj = HSO p (fromMaybe [] haskellData) - finalizer | isJust haskellData = Just $ finalizeHaskellID p new_sptr - | immortal = Nothing + finalizer | immortal = Nothing | otherwise = Just $ finalizeID p new_sptr wptr <- mkWeakPtr haskellObj finalizer new_sptr <- newStablePtr wptr setHaskellPart p new_sptr (if immortal then 1 else 0) - case haskellData of - Just _ -> haskellObject_retain p - Nothing -> retainObject p - - return $ ID haskellObj + return $ do + -- retain the object, but do it outside the + -- lock because the retain IMP may need the lock. + retainObject p + return $ ID haskellObj finalizeID :: Ptr ObjCObject -> StablePtr (Weak HSO) -> IO () finalizeID cObj sptr = do - withMVar objectMapLock $ \_ -> removeHaskellPart cObj sptr + withObjectMapLock "finalizeID" $ removeHaskellPart cObj sptr + releaseObjectWithPool cObj freeStablePtr sptr -finalizeHaskellID :: Ptr ObjCObject -> StablePtr (Weak HSO) -> IO () -finalizeHaskellID cObj sptr = do - withMVar objectMapLock $ \_ -> removeHaskellPart cObj sptr - extraRefs <- nsExtraRefCount cObj - -- putStrLn "destroy haskelll object" - assert (extraRefs == 0) (deallocObjectWithPool cObj) - freeStablePtr sptr - --- makeNewHaskellData p = do stable <- getNewHaskellData p if (castStablePtrToPtr stable == nullPtr) @@ -204,77 +219,74 @@ freeStablePtr stable return (Just dat) -haskellObject_retain_IMP :: FFICif -> Ptr () -> Ptr (Ptr ()) -> IO (Ptr ObjCObject) -haskellObject_retain_IMP cif ret args = do +haskellObject_retain_IMP :: Ptr ObjCObject -> FFICif -> Ptr () -> Ptr (Ptr ()) -> IO (Ptr ObjCObject) +haskellObject_retain_IMP super cif ret args = do selfPtr <- peekElemOff args 0 self <- peek (castPtr selfPtr) :: IO (Ptr ObjCObject) poke (castPtr ret) self -- retain returns self - -- putStrLn "retain haskell object_IMP" - withMVar objectMapLock $ \_ -> haskellObject_retain self + dPutWords ["haskellObject_retain_IMP", show super, "<FFICif>", show ret, show args] + haskellObject_retain self super return nullPtr -- no exception -haskellObject_retain self = do - -- putStrLn "retain haskell object" - nsIncrementExtraRefCount self +haskellObject_retain self super = do + dPutWords ["haskellObject_retain", show self, show super] + retainSuper self super + dPutStrLn "retained super" - stablePtrToHaskellSelf <- getRetainedHaskellPart self - when (castStablePtrToPtr stablePtrToHaskellSelf == nullPtr) $ do - stableWeakPtrToHaskellSelf <- getHaskellPart self - when (castStablePtrToPtr stableWeakPtrToHaskellSelf /= nullPtr) $ do - weakPtrToHaskellSelf <- deRefStablePtr stableWeakPtrToHaskellSelf - mbHaskellSelf <- deRefWeak weakPtrToHaskellSelf - case mbHaskellSelf of - Just haskellSelf -> do - stablePtrToHaskellSelf <- newStablePtr haskellSelf - setRetainedHaskellPart self stablePtrToHaskellSelf - Nothing -> - -- The weak pointer will only be dealloced when there are - -- no known references from ObjC and no references from Haskell. - -- So if we get here, it's not my bug (hopefully). - -- When an object is exported (returned or passed as a parameter) - -- from Haskell, it is retained and autoreleased, so passing an - -- object from Haskell to Objective C and immediately forgetting - -- the reference (before ObjC has a chance to retain it) is safe. - - error "Error: Retaining Haskell Object that has already been released" + withObjectMapLock "haskellObject_retain" $ do + stablePtrToHaskellSelf <- getRetainedHaskellPart self + when (castStablePtrToPtr stablePtrToHaskellSelf == nullPtr) $ do + stableWeakPtrToHaskellSelf <- getHaskellPart self + when (castStablePtrToPtr stableWeakPtrToHaskellSelf /= nullPtr) $ do + weakPtrToHaskellSelf <- deRefStablePtr stableWeakPtrToHaskellSelf + mbHaskellSelf <- deRefWeak weakPtrToHaskellSelf + case mbHaskellSelf of + Just haskellSelf -> do + stablePtrToHaskellSelf <- newStablePtr haskellSelf + setRetainedHaskellPart self stablePtrToHaskellSelf + Nothing -> + -- The weak pointer will only be dealloced when there are + -- no known references from ObjC and no references from Haskell. + -- So if we get here, it's not my bug (hopefully). + -- When an object is exported (returned or passed as a parameter) + -- from Haskell, it is retained and autoreleased, so passing an + -- object from Haskell to Objective C and immediately forgetting + -- the reference (before ObjC has a chance to retain it) is safe. + + error "Error: Retaining Haskell Object that has already been released" -haskellObject_release_IMP :: FFICif -> Ptr () -> Ptr (Ptr ()) -> IO (Ptr ObjCObject) -haskellObject_release_IMP cif ret args = do +haskellObject_release_IMP :: Ptr ObjCObject -> FFICif -> Ptr () -> Ptr (Ptr ()) -> IO (Ptr ObjCObject) +haskellObject_release_IMP super cif ret args = do selfPtr <- peekElemOff args 0 self <- peek (castPtr selfPtr) :: IO (Ptr ObjCObject) - -- putStrLn "release haskell object_IMP" - withMVar objectMapLock $ \_ -> haskellObject_release self + dPutWords ["haskellObject_release_IMP", show super, "<FFICif>", show ret, show args] + haskellObject_release super self return nullPtr -- no exception -haskellObject_release self = do - -- putStrLn "release haskell object" - wasZero <- nsDecrementExtraRefCountWasZero self - -- nobody else should call NSDecrementExtraRefCountWasZero anyway, - -- and we're protected from ourselves by the objectMapLock - -- ==> no race condition here - refCount <- nsExtraRefCount self +haskellObject_release super self = do + dPutWords ["haskellObject_release", show super, show self] + retainCount+1 <- retainCount self + -- retainCount+1 because we want to know the retainCount after we + -- release; if it's about to become zero, then we won't be + -- able to call retainCount on self after the call to releaseSuper. + releaseSuper self super + -- retainCount should now contain the current retain count. - when (refCount == 0) $ do + when (retainCount == 1) $ withObjectMapLock "haskellObject_release" $ do -- no extra references -- Only the reference from the Haskell part remains, -- which means we do no longer want to have a stable pointer -- (if we have one, that is) - stablePtrToHaskellSelf <- getRetainedHaskellPart self - when (castStablePtrToPtr stablePtrToHaskellSelf /= nullPtr) $ do - freeStablePtr stablePtrToHaskellSelf - setRetainedHaskellPart self (castPtrToStablePtr nullPtr) - - when (wasZero /= 0) $ do - deallocObject self + replaceRetainedHaskellPart self (castPtrToStablePtr nullPtr) -- this is the implementation of the __getHaskellData__ selector. getHaskellData_IMP :: Ptr ObjCObject -> Maybe (IO Dynamic) -> FFICif -> Ptr () -> Ptr (Ptr ()) -> IO (Ptr ObjCObject) getHaskellData_IMP super mbDat cif ret args = do - -- putStrLn "__getHaskellData__" selfPtr <- peekElemOff args 0 self <- peek (castPtr selfPtr) :: IO (Ptr ObjCObject) + dPutWords ["__getHaskellData__", show self, show super] superDataStable <- getNewHaskellDataForClass self super superData <- if castStablePtrToPtr superDataStable == nullPtr then do Modified: branches/objc2/hoc/HOC/HOC/NewClass.hs ============================================================================== --- branches/objc2/hoc/HOC/HOC/NewClass.hs (original) +++ branches/objc2/hoc/HOC/HOC/NewClass.hs Fri Jan 9 16:57:06 2009 @@ -95,20 +95,20 @@ return list retainSelector = getSelectorForName "retain" -retainCif = getCifForSelector (undefined :: ID () -> IO (ID ())) +retainCif = getCifForSelector (undefined :: Class () -> ID () -> IO (ID ())) releaseSelector = getSelectorForName "release" -releaseCif = getCifForSelector (undefined :: ID () -> IO ()) +releaseCif = getCifForSelector (undefined :: Class () -> ID () -> IO ()) getHaskellDataSelector = getSelectorForName "__getHaskellData__" getHaskellDataCif = getCifForSelector (undefined :: Class () -> ID () -> IO (ID ())) -- actually -> IO (Ptr ()) ... -setHaskellRetainMethod methodList idx = - setMethodInList methodList idx retainSelector "@@:" retainCif haskellObject_retain_IMP +setHaskellRetainMethod methodList idx super = + setMethodInList methodList idx retainSelector "@@:" retainCif (haskellObject_retain_IMP super) -setHaskellReleaseMethod methodList idx = - setMethodInList methodList idx releaseSelector "v@:" releaseCif haskellObject_release_IMP +setHaskellReleaseMethod methodList idx super = + setMethodInList methodList idx releaseSelector "v@:" releaseCif (haskellObject_release_IMP super) setHaskellDataMethod methodList idx super mbDat = setMethodInList methodList idx getHaskellDataSelector "^v@:#" getHaskellDataCif (getHaskellData_IMP super mbDat) Modified: branches/objc2/hoc/HOC_cbits/MemoryManagement.h ============================================================================== --- branches/objc2/hoc/HOC_cbits/MemoryManagement.h (original) +++ branches/objc2/hoc/HOC_cbits/MemoryManagement.h Fri Jan 9 16:57:06 2009 @@ -8,6 +8,11 @@ void retainObject(id obj); void releaseObject(id obj); + +void retainSuper(id obj, Class cls); +void releaseSuper(id obj, Class cls); +unsigned int retainCount(id obj); + void deallocObject(id obj); void autoreleaseObject(id obj); Modified: branches/objc2/hoc/HOC_cbits/MemoryManagement.m ============================================================================== --- branches/objc2/hoc/HOC_cbits/MemoryManagement.m (original) +++ branches/objc2/hoc/HOC_cbits/MemoryManagement.m Fri Jan 9 16:57:06 2009 @@ -1,3 +1,11 @@ +#ifdef GNUSTEP +#include <objc/objc-api.h> +#else +#include <objc/objc-runtime.h> +#endif + +#include <stdlib.h> + #include "MemoryManagement.h" #define DO_LOG 0 @@ -44,6 +52,7 @@ static SEL selRetain = 0; static SEL selRelease = 0; +static SEL selRetainCount = 0; static SEL selDealloc = 0; static SEL selAutorelease = 0; static SEL selAlloc = 0; @@ -55,7 +64,7 @@ if(!selRetain) selRetain = getSelectorForName("retain"); #if DO_LOG - printf("retain %p, %p\n",obj,obj->class_pointer); + printf("retain %p, %p\n",obj,obj->isa); #endif objc_msgSend(obj,selRetain); } @@ -65,9 +74,59 @@ if(!selRelease) selRelease = getSelectorForName("release"); #if DO_LOG - printf("release %p, %p\n",obj,obj->class_pointer); + printf("release %p, %p\n",obj,obj->isa); #endif objc_msgSend(obj,selRelease); +} + +void retainSuper(id obj, Class cls) +{ + if(!selRetain) + selRetain = getSelectorForName("retain"); + +#if DO_LOG + printf("retain super %p, %p\n",obj,cls); +#endif + + struct objc_super * super = calloc(1, sizeof(struct objc_super)); + + super->receiver = obj; + super->super_class = cls; + + objc_msgSendSuper(super, selRetain); +} + +void releaseSuper(id obj, Class cls) +{ + if(!selRelease) + selRelease = getSelectorForName("release"); + +#if DO_LOG + printf("release super %p, %p\n",obj,cls); +#endif + + struct objc_super * super = calloc(1, sizeof(struct objc_super)); + + super->receiver = obj; + super->super_class = cls; + + objc_msgSendSuper(super, selRelease); +} + +unsigned int retainCount(id obj) { + unsigned int rc; + +#if DO_LOG + printf("retainCount %p = ",obj); +#endif + if(!selRetainCount) + selRetainCount = getSelectorForName("retainCount"); + + rc = (unsigned int) objc_msgSend(obj,selRetainCount); +#if DO_LOG + printf("%d\n",rc); +#endif + return rc; } void deallocObject(id obj) |