
Hello, I have a question about the semantics of finalizers of ForeignPtrs: If an in scope value has a reference to a ForeignPtr is the foreign object always kept alive, even if that reference is not used? Or do I always need to wrap each computation, which needs to have the foreign object alive, inside withForeignPtr? To make my question more concrete, I will give a simplified example from my usb library (a high-level binding to the libusb C library): https://github.com/basvandijk/usb To use the library you first need to initialize libusb which will yield a context: newtype Ctx = Ctx {getCtxFrgnPtr ∷ (ForeignPtr C'libusb_context)} When you're finished with libusb you need to exit the library. This is done automatically using a ForeignPtr: newCtx ∷ IO Ctx newCtx = mask_ $ do ctxPtr ← libusb_init Ctx <$> newForeignPtr p'libusb_exit ctxPtr where libusb_init ∷ IO (Ptr C'libusb_context) libusb_init = alloca $ \ctxPtrPtr → do handleUSBException $ c'libusb_init ctxPtrPtr peek ctxPtrPtr I provide a handy internal utility function for accessing the pointer to the context: withCtxPtr ∷ Ctx → (Ptr C'libusb_context → IO α) → IO α withCtxPtr = withForeignPtr ∘ getCtxFrgnPtr Because this function uses withForeignPtr it's guaranteed that the context is kep alive during the duration of the given function. For example, in the following, it is guaranteed that c'libusb_set_debug is always given a live context: setDebug ∷ Ctx → Verbosity → IO () setDebug ctx verbosity = withCtxPtr ctx $ \ctxPtr → c'libusb_set_debug ctxPtr $ genFromEnum verbosity There are also types which are derived from a context. For example: getDevices ∷ Ctx → IO [Device] is a function which given a context returns a list of USB devices currently attached to your system. The requirement is that when you need to use a device the context has to be kept alive. Therefor I keep a reference to the context inside the device: data Device = Device { getCtx ∷ !Ctx , getDevFrgnPtr ∷ !(ForeignPtr C'libusb_device) , deviceDesc ∷ !DeviceDesc } The idea is that as long as you have a reference to a device you also keep the context alive because the device references the context. (Note that a device, in turn, also contains a ForeignPtr) getDevices ∷ Ctx → IO [Device] getDevices ctx = ... where mkDev ∷ Ptr C'libusb_device → IO Device mkDev devPtr = liftA2 (Device ctx) (newForeignPtr p'libusb_unref_device devPtr) (getDeviceDesc devPtr) Again I provide a handy internal utility function for accessing the pointer to a device: withDevicePtr ∷ Device → (Ptr C'libusb_device → IO α) → IO α withDevicePtr = withForeignPtr ∘ getDevFrgnPtr And now my question. Is it guaranteed that when the device is in scope the context is always kept alive? For example, is the context kept alive in the following: openDevice ∷ Device → IO DeviceHandle openDevice dev = withDevicePtr dev $ \devPtr → alloca $ \devHndlPtrPtr → do handleUSBException $ c'libusb_open devPtr devHndlPtrPtr DeviceHandle dev <$> peek devHndlPtrPtr Or do I also need to call withCtxPtr inside withDevicePtr as such: withDevicePtr (Device ctx devFP _) f = withCtxPtr ctx $ \_ → withForeignPtr devFP f Thanks, Bas