summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Feuer <david.feuer@gmail.com>2018-08-29 16:34:21 -0400
committerDavid Feuer <David.Feuer@gmail.com>2018-08-29 16:34:22 -0400
commitf48e276a5ba68d8b6fcb4a558022581fb30f9326 (patch)
tree8ea324b84d137d59cda566d6a559016d2c7437ea
parent65eec9cfd4410c0e30b0ed06116c15f8ce3de49d (diff)
downloadhaskell-f48e276a5ba68d8b6fcb4a558022581fb30f9326.tar.gz
Finish stable split
Long ago, the stable name table and stable pointer tables were one. Now, they are separate, and have significantly different implementations. I believe the time has come to finish the split that began in #7674. * Divide `rts/Stable` into `rts/StableName` and `rts/StablePtr`. * Give each table its own mutex. * Add FFI functions `hs_lock_stable_ptr_table` and `hs_unlock_stable_ptr_table` and document them. These are intended to replace the previously undocumented `hs_lock_stable_tables` and `hs_lock_stable_tables`, which are now documented as deprecated synonyms. * Make `eqStableName#` use pointer equality instead of unnecessarily comparing stable name table indices. Reviewers: simonmar, bgamari, erikd Reviewed By: bgamari Subscribers: rwbarton, carter GHC Trac Issues: #15555 Differential Revision: https://phabricator.haskell.org/D5084
-rw-r--r--compiler/codeGen/StgCmmPrim.hs21
-rw-r--r--docs/users_guide/8.8.1-notes.rst8
-rw-r--r--docs/users_guide/ffi-chap.rst40
-rw-r--r--includes/HsFFI.h18
-rw-r--r--includes/Rts.h3
-rw-r--r--includes/rts/StableName.h (renamed from includes/rts/Stable.h)18
-rw-r--r--includes/rts/StablePtr.h35
-rw-r--r--includes/stg/MiscClosures.h4
-rw-r--r--rts/Adjustor.c2
-rw-r--r--rts/Globals.c2
-rw-r--r--rts/HsFFI.c20
-rw-r--r--rts/Interpreter.c2
-rw-r--r--rts/Linker.c2
-rw-r--r--rts/RetainerProfile.c7
-rw-r--r--rts/RtsAPI.c2
-rw-r--r--rts/RtsStartup.c13
-rw-r--r--rts/RtsSymbols.c2
-rw-r--r--rts/Schedule.c12
-rw-r--r--rts/StableName.c (renamed from rts/Stable.c)287
-rw-r--r--rts/StableName.h31
-rw-r--r--rts/StablePtr.c329
-rw-r--r--rts/StablePtr.h (renamed from rts/Stable.h)27
-rw-r--r--rts/StaticPtrTable.c2
-rw-r--r--rts/TopHandler.c2
-rw-r--r--rts/TopHandler.h1
-rw-r--r--rts/posix/Signals.c1
-rw-r--r--rts/rts.cabal.in6
-rw-r--r--rts/sm/Compact.c8
-rw-r--r--rts/sm/GC.c23
-rw-r--r--utils/deriveConstants/Main.hs2
30 files changed, 586 insertions, 344 deletions
diff --git a/compiler/codeGen/StgCmmPrim.hs b/compiler/codeGen/StgCmmPrim.hs
index a6e13086aa..266ab3a0f6 100644
--- a/compiler/codeGen/StgCmmPrim.hs
+++ b/compiler/codeGen/StgCmmPrim.hs
@@ -352,14 +352,6 @@ emitPrimOp dflags [res] ByteArrayContents_Char [arg]
emitPrimOp dflags [res] StableNameToIntOp [arg]
= emitAssign (CmmLocal res) (cmmLoadIndexW dflags arg (fixedHdrSizeW dflags) (bWord dflags))
--- #define eqStableNamezh(r,sn1,sn2) \
--- (r = (((StgStableName *)sn1)->sn == ((StgStableName *)sn2)->sn))
-emitPrimOp dflags [res] EqStableNameOp [arg1,arg2]
- = emitAssign (CmmLocal res) (CmmMachOp (mo_wordEq dflags) [
- cmmLoadIndexW dflags arg1 (fixedHdrSizeW dflags) (bWord dflags),
- cmmLoadIndexW dflags arg2 (fixedHdrSizeW dflags) (bWord dflags)
- ])
-
emitPrimOp dflags [res] ReallyUnsafePtrEqualityOp [arg1,arg2]
= emitAssign (CmmLocal res) (CmmMachOp (mo_wordEq dflags) [arg1,arg2])
@@ -1405,9 +1397,22 @@ translateOp dflags SameMutableArrayArrayOp= Just (mo_wordEq dflags)
translateOp dflags SameSmallMutableArrayOp= Just (mo_wordEq dflags)
translateOp dflags SameTVarOp = Just (mo_wordEq dflags)
translateOp dflags EqStablePtrOp = Just (mo_wordEq dflags)
+-- See Note [Comparing stable names]
+translateOp dflags EqStableNameOp = Just (mo_wordEq dflags)
translateOp _ _ = Nothing
+-- Note [Comparing stable names]
+-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+--
+-- A StableName# is actually a pointer to a stable name object (SNO)
+-- containing an index into the stable name table (SNT). We
+-- used to compare StableName#s by following the pointers to the
+-- SNOs and checking whether they held the same SNT indices. However,
+-- this is not necessary: there is a one-to-one correspondence
+-- between SNOs and entries in the SNT, so simple pointer equality
+-- does the trick.
+
-- These primops are implemented by CallishMachOps, because they sometimes
-- turn into foreign calls depending on the backend.
diff --git a/docs/users_guide/8.8.1-notes.rst b/docs/users_guide/8.8.1-notes.rst
index da236f923d..50323b3939 100644
--- a/docs/users_guide/8.8.1-notes.rst
+++ b/docs/users_guide/8.8.1-notes.rst
@@ -48,6 +48,14 @@ Compiler
Runtime system
~~~~~~~~~~~~~~
+- Add and document new FFI functions ``hs_lock_stable_ptr_table``
+ and ``hs_unlock_stable_ptr_table``. These replace the undocumented
+ functions ``hs_lock_stable_tables`` and ``hs_unlock_stable_tables``,
+ respectively. The latter should now be considered deprecated.
+
+- Document the heretofore undocumented FFI function
+ ``hs_free_stable_ptr_unsafe``, used in conjunction with manual
+ locking and unlocking.
Template Haskell
~~~~~~~~~~~~~~~~
diff --git a/docs/users_guide/ffi-chap.rst b/docs/users_guide/ffi-chap.rst
index 0b107de234..62bad46781 100644
--- a/docs/users_guide/ffi-chap.rst
+++ b/docs/users_guide/ffi-chap.rst
@@ -245,6 +245,46 @@ allocated until ``hs_exit()`` is called. If you call it too often, the
worst that can happen is that the next call to a Haskell function incurs
some extra overhead.
+.. _ffi-stable-ptr-extras:
+
+Freeing many stable pointers efficiently
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The standard function ``hs_free_stable_ptr`` locks the stable pointer
+table, frees the given stable pointer, and then unlocks the stable pointer
+table again. When freeing many stable pointers at once, it is usually
+more efficient to lock and unlock the table only once.
+
+.. code-block:: c
+
+ extern void hs_lock_stable_ptr_table (void);
+
+ extern void hs_unlock_stable_ptr_table (void);
+
+ extern void hs_free_stable_ptr_unsafe (HsStablePtr sp);
+
+``hs_free_stable_ptr_unsafe`` must be used *only* when the table has been
+locked using ``hs_lock_stable_ptr_table``. It must be unlocked afterwards
+using ``hs_unlock_stable_ptr_table``. The Haskell garbage collector cannot
+run while the table is locked, so it should be unlocked promptly. The
+following operations are forbidden while the stable pointer table is locked:
+
+* Calling any Haskell function, whether or not that function
+ manipulates stable pointers.
+
+* Calling any FFI function that deals with the stable pointer table
+ except for arbitrarily many calls to ``hs_free_stable_ptr_unsafe``
+ and the final call to ``hs_unlock_stable_ptr_table``.
+
+* Calling ``hs_free_fun_ptr``.
+
+.. note::
+
+ GHC versions before 8.8 defined undocumented functions
+ ``hs_lock_stable_tables`` and ``hs_unlock_stable_tables`` instead
+ of ``hs_lock_stable_ptr_table`` and ``hs_unlock_stable_ptr_table``.
+ Those names are now deprecated.
+
.. _ffi-ghc:
Using the FFI with GHC
diff --git a/includes/HsFFI.h b/includes/HsFFI.h
index dea365cb65..84976474f5 100644
--- a/includes/HsFFI.h
+++ b/includes/HsFFI.h
@@ -101,8 +101,26 @@ extern void hs_thread_done (void);
extern void hs_perform_gc (void);
+// Lock the stable pointer table. The table must be unlocked
+// again before calling any Haskell functions, even if those
+// functions do not manipulate stable pointers. The Haskell
+// garbage collector will not be able to run until this lock
+// is released! It is also forbidden to call hs_free_fun_ptr
+// or any stable pointer-related FFI functions other than
+// hs_free_stable_ptr_unsafe while the table is locked.
+extern void hs_lock_stable_ptr_table (void);
+
+// A deprecated synonym.
extern void hs_lock_stable_tables (void);
+
+// Unlock the stable pointer table.
+extern void hs_unlock_stable_ptr_table (void);
+
+// A deprecated synonym.
extern void hs_unlock_stable_tables (void);
+
+// Free a stable pointer assuming that the stable pointer
+// table is already locked.
extern void hs_free_stable_ptr_unsafe (HsStablePtr sp);
extern void hs_free_stable_ptr (HsStablePtr sp);
diff --git a/includes/Rts.h b/includes/Rts.h
index fc70479eb6..eb11536c19 100644
--- a/includes/Rts.h
+++ b/includes/Rts.h
@@ -198,7 +198,8 @@ void _assertFail(const char *filename, unsigned int linenum)
#include "rts/Linker.h"
#include "rts/Ticky.h"
#include "rts/Timer.h"
-#include "rts/Stable.h"
+#include "rts/StablePtr.h"
+#include "rts/StableName.h"
#include "rts/TTY.h"
#include "rts/Utils.h"
#include "rts/PrimFloat.h"
diff --git a/includes/rts/Stable.h b/includes/rts/StableName.h
index 4550ad6117..d43ffcb2f6 100644
--- a/includes/rts/Stable.h
+++ b/includes/rts/StableName.h
@@ -2,7 +2,7 @@
*
* (c) The GHC Team, 1998-2009
*
- * Stable Pointers
+ * Stable Names
*
* Do not #include this file directly: #include "Rts.h" instead.
*
@@ -13,9 +13,6 @@
#pragma once
-EXTERN_INLINE StgPtr deRefStablePtr (StgStablePtr stable_ptr);
-StgStablePtr getStablePtr (StgPtr p);
-
/* -----------------------------------------------------------------------------
PRIVATE from here.
-------------------------------------------------------------------------- */
@@ -32,17 +29,4 @@ typedef struct {
// free
} snEntry;
-typedef struct {
- StgPtr addr; // Haskell object when entry is in use, next free
- // entry (NULL when this is the last free entry)
- // otherwise.
-} spEntry;
-
extern DLL_IMPORT_RTS snEntry *stable_name_table;
-extern DLL_IMPORT_RTS spEntry *stable_ptr_table;
-
-EXTERN_INLINE
-StgPtr deRefStablePtr(StgStablePtr sp)
-{
- return stable_ptr_table[(StgWord)sp].addr;
-}
diff --git a/includes/rts/StablePtr.h b/includes/rts/StablePtr.h
new file mode 100644
index 0000000000..0d3642fbfe
--- /dev/null
+++ b/includes/rts/StablePtr.h
@@ -0,0 +1,35 @@
+/* -----------------------------------------------------------------------------
+ *
+ * (c) The GHC Team, 1998-2009
+ *
+ * Stable Pointers
+ *
+ * Do not #include this file directly: #include "Rts.h" instead.
+ *
+ * To understand the structure of the RTS headers, see the wiki:
+ * http://ghc.haskell.org/trac/ghc/wiki/Commentary/SourceTree/Includes
+ *
+ * ---------------------------------------------------------------------------*/
+
+#pragma once
+
+EXTERN_INLINE StgPtr deRefStablePtr (StgStablePtr stable_ptr);
+StgStablePtr getStablePtr (StgPtr p);
+
+/* -----------------------------------------------------------------------------
+ PRIVATE from here.
+ -------------------------------------------------------------------------- */
+
+typedef struct {
+ StgPtr addr; // Haskell object when entry is in use, next free
+ // entry (NULL when this is the last free entry)
+ // otherwise.
+} spEntry;
+
+extern DLL_IMPORT_RTS spEntry *stable_ptr_table;
+
+EXTERN_INLINE
+StgPtr deRefStablePtr(StgStablePtr sp)
+{
+ return stable_ptr_table[(StgWord)sp].addr;
+}
diff --git a/includes/stg/MiscClosures.h b/includes/stg/MiscClosures.h
index 5328ed3f4a..c13b5ff052 100644
--- a/includes/stg/MiscClosures.h
+++ b/includes/stg/MiscClosures.h
@@ -514,8 +514,10 @@ extern StgWord RTS_VAR(atomic_modify_mutvar_mutex);
// RtsFlags
extern StgWord RTS_VAR(RtsFlags); // bogus type
-// Stable.c
+// StablePtr.c
extern StgWord RTS_VAR(stable_ptr_table);
+
+// StableName.c
extern StgWord RTS_VAR(stable_name_table);
// Profiling.c
diff --git a/rts/Adjustor.c b/rts/Adjustor.c
index a1bfeb96b7..476d63140e 100644
--- a/rts/Adjustor.c
+++ b/rts/Adjustor.c
@@ -40,7 +40,7 @@ Haskell side.
#include "Rts.h"
#include "RtsUtils.h"
-#include "Stable.h"
+#include "StablePtr.h"
#if defined(USE_LIBFFI_FOR_ADJUSTORS)
#include "ffi.h"
diff --git a/rts/Globals.c b/rts/Globals.c
index 66c17d0f96..c9980d9a3a 100644
--- a/rts/Globals.c
+++ b/rts/Globals.c
@@ -21,7 +21,7 @@
#include "Rts.h"
#include "Globals.h"
-#include "Stable.h"
+#include "StablePtr.h"
typedef enum {
GHCConcSignalSignalHandlerStore,
diff --git a/rts/HsFFI.c b/rts/HsFFI.c
index 8fae246111..e482932193 100644
--- a/rts/HsFFI.c
+++ b/rts/HsFFI.c
@@ -10,7 +10,7 @@
#include "HsFFI.h"
#include "Rts.h"
-#include "Stable.h"
+#include "StablePtr.h"
#include "Task.h"
// hs_init and hs_exit are defined in RtsStartup.c
@@ -28,14 +28,28 @@ hs_perform_gc(void)
performMajorGC();
}
+// Lock the stable pointer table
+void hs_lock_stable_ptr_table (void)
+{
+ stablePtrLock();
+}
+
+// Deprecated version of hs_lock_stable_ptr_table
void hs_lock_stable_tables (void)
{
- stableLock();
+ stablePtrLock();
+}
+
+// Unlock the stable pointer table
+void hs_unlock_stable_ptr_table (void)
+{
+ stablePtrUnlock();
}
+// Deprecated version of hs_unlock_stable_ptr_table
void hs_unlock_stable_tables (void)
{
- stableUnlock();
+ stablePtrUnlock();
}
void
diff --git a/rts/Interpreter.c b/rts/Interpreter.c
index 9eb6560a8c..a3b179a4be 100644
--- a/rts/Interpreter.c
+++ b/rts/Interpreter.c
@@ -16,7 +16,7 @@
#include "Schedule.h"
#include "Updates.h"
#include "Prelude.h"
-#include "Stable.h"
+#include "StablePtr.h"
#include "Printer.h"
#include "Profiling.h"
#include "Disassembler.h"
diff --git a/rts/Linker.c b/rts/Linker.c
index aa6ec7fe7a..934b90782d 100644
--- a/rts/Linker.c
+++ b/rts/Linker.c
@@ -22,7 +22,7 @@
#include "StgPrimFloat.h" // for __int_encodeFloat etc.
#include "Proftimer.h"
#include "GetEnv.h"
-#include "Stable.h"
+#include "StablePtr.h"
#include "RtsSymbols.h"
#include "RtsSymbolInfo.h"
#include "Profiling.h"
diff --git a/rts/RetainerProfile.c b/rts/RetainerProfile.c
index 8a64de9e14..d67eeb4834 100644
--- a/rts/RetainerProfile.c
+++ b/rts/RetainerProfile.c
@@ -30,7 +30,8 @@
#include "Stats.h"
#include "ProfHeap.h"
#include "Apply.h"
-#include "Stable.h" /* markStableTables */
+#include "StablePtr.h" /* markStablePtrTable */
+#include "StableName.h" /* rememberOldStableNameAddresses */
#include "sm/Storage.h" // for END_OF_STATIC_LIST
/* Note [What is a retainer?]
@@ -1693,7 +1694,9 @@ computeRetainerSet( void )
}
// Consider roots from the stable ptr table.
- markStableTables(retainRoot, NULL);
+ markStablePtrTable(retainRoot, NULL);
+ // Remember old stable name addresses.
+ rememberOldStableNameAddresses ();
// The following code resets the rs field of each unvisited mutable
// object (computing sumOfNewCostExtra and updating costArray[] when
diff --git a/rts/RtsAPI.c b/rts/RtsAPI.c
index 8fd1917392..9396dccc07 100644
--- a/rts/RtsAPI.c
+++ b/rts/RtsAPI.c
@@ -15,7 +15,7 @@
#include "Prelude.h"
#include "Schedule.h"
#include "Capability.h"
-#include "Stable.h"
+#include "StablePtr.h"
#include "Threads.h"
#include "Weak.h"
diff --git a/rts/RtsStartup.c b/rts/RtsStartup.c
index 0cb1ff9700..5e5aef3505 100644
--- a/rts/RtsStartup.c
+++ b/rts/RtsStartup.c
@@ -26,7 +26,8 @@
#include "ThreadLabels.h"
#include "sm/BlockAlloc.h"
#include "Trace.h"
-#include "Stable.h"
+#include "StableName.h"
+#include "StablePtr.h"
#include "StaticPtrTable.h"
#include "Hash.h"
#include "Profiling.h"
@@ -243,7 +244,10 @@ hs_init_ghc(int *argc, char **argv[], RtsConfig rts_config)
initStorage();
/* initialise the stable pointer table */
- initStableTables();
+ initStablePtrTable();
+
+ /* initialise the stable name table */
+ initStableNameTable();
/* Add some GC roots for things in the base package that the RTS
* knows about. We don't know whether these turn out to be CAFs
@@ -451,7 +455,10 @@ hs_exit_(bool wait_foreign)
exitTopHandler();
/* free the stable pointer table */
- exitStableTables();
+ exitStablePtrTable();
+
+ /* free the stable name table */
+ exitStableNameTable();
#if defined(DEBUG)
/* free the thread label table */
diff --git a/rts/RtsSymbols.c b/rts/RtsSymbols.c
index 79ab3f1d12..5091c90dad 100644
--- a/rts/RtsSymbols.c
+++ b/rts/RtsSymbols.c
@@ -615,6 +615,8 @@
SymI_HasProto(hs_exit_nowait) \
SymI_HasProto(hs_set_argv) \
SymI_HasProto(hs_perform_gc) \
+ SymI_HasProto(hs_lock_stable_ptr_table) \
+ SymI_HasProto(hs_unlock_stable_ptr_table) \
SymI_HasProto(hs_lock_stable_tables) \
SymI_HasProto(hs_unlock_stable_tables) \
SymI_HasProto(hs_free_stable_ptr) \
diff --git a/rts/Schedule.c b/rts/Schedule.c
index cf975b51dd..0444f0ca15 100644
--- a/rts/Schedule.c
+++ b/rts/Schedule.c
@@ -41,7 +41,8 @@
#include "Timer.h"
#include "ThreadPaused.h"
#include "Messages.h"
-#include "Stable.h"
+#include "StablePtr.h"
+#include "StableName.h"
#include "TopHandler.h"
#if defined(HAVE_SYS_TYPES_H)
@@ -1964,7 +1965,8 @@ forkProcess(HsStablePtr *entry
// inconsistent state in the child. See also #1391.
ACQUIRE_LOCK(&sched_mutex);
ACQUIRE_LOCK(&sm_mutex);
- ACQUIRE_LOCK(&stable_mutex);
+ ACQUIRE_LOCK(&stable_ptr_mutex);
+ ACQUIRE_LOCK(&stable_name_mutex);
ACQUIRE_LOCK(&task->lock);
for (i=0; i < n_capabilities; i++) {
@@ -1989,7 +1991,8 @@ forkProcess(HsStablePtr *entry
RELEASE_LOCK(&sched_mutex);
RELEASE_LOCK(&sm_mutex);
- RELEASE_LOCK(&stable_mutex);
+ RELEASE_LOCK(&stable_ptr_mutex);
+ RELEASE_LOCK(&stable_name_mutex);
RELEASE_LOCK(&task->lock);
#if defined(THREADED_RTS)
@@ -2012,7 +2015,8 @@ forkProcess(HsStablePtr *entry
#if defined(THREADED_RTS)
initMutex(&sched_mutex);
initMutex(&sm_mutex);
- initMutex(&stable_mutex);
+ initMutex(&stable_ptr_mutex);
+ initMutex(&stable_name_mutex);
initMutex(&task->lock);
for (i=0; i < n_capabilities; i++) {
diff --git a/rts/Stable.c b/rts/StableName.c
index 71eaf1a242..abe7b692e0 100644
--- a/rts/Stable.c
+++ b/rts/StableName.c
@@ -4,7 +4,7 @@
*
* (c) The GHC Team, 1998-2002
*
- * Stable names and stable pointers.
+ * Stable names
*
* ---------------------------------------------------------------------------*/
@@ -15,112 +15,20 @@
#include "Hash.h"
#include "RtsUtils.h"
#include "Trace.h"
-#include "Stable.h"
+#include "StableName.h"
#include <string.h>
-/* Comment from ADR's implementation in old RTS:
-
- This files (together with @ghc/runtime/storage/PerformIO.lhc@ and a
- small change in @HpOverflow.lc@) consists of the changes in the
- runtime system required to implement "Stable Pointers". But we're
- getting a bit ahead of ourselves --- what is a stable pointer and what
- is it used for?
-
- When Haskell calls C, it normally just passes over primitive integers,
- floats, bools, strings, etc. This doesn't cause any problems at all
- for garbage collection because the act of passing them makes a copy
- from the heap, stack or wherever they are onto the C-world stack.
- However, if we were to pass a heap object such as a (Haskell) @String@
- and a garbage collection occured before we finished using it, we'd run
- into problems since the heap object might have been moved or even
- deleted.
-
- So, if a C call is able to cause a garbage collection or we want to
- store a pointer to a heap object between C calls, we must be careful
- when passing heap objects. Our solution is to keep a table of all
- objects we've given to the C-world and to make sure that the garbage
- collector collects these objects --- updating the table as required to
- make sure we can still find the object.
-
-
- Of course, all this rather begs the question: why would we want to
- pass a boxed value?
-
- One very good reason is to preserve laziness across the language
- interface. Rather than evaluating an integer or a string because it
- {\em might\/} be required by the C function, we can wait until the C
- function actually wants the value and then force an evaluation.
-
- Another very good reason (the motivating reason!) is that the C code
- might want to execute an object of sort $IO ()$ for the side-effects
- it will produce. For example, this is used when interfacing to an X
- widgets library to allow a direct implementation of callbacks.
-
- One final reason is that we may want to store composite Haskell
- values in data structures implemented in the C side. Serializing and
- deserializing these structures into unboxed form suitable for C may
- be more expensive than maintaining the extra layer of indirection of
- stable pointers.
-
- The @makeStablePointer :: a -> IO (StablePtr a)@ function
- converts a value into a stable pointer. It is part of the @PrimIO@
- monad, because we want to be sure we don't allocate one twice by
- accident, and then only free one of the copies.
-
- \begin{verbatim}
- makeStablePtr# :: a -> State# RealWorld -> (# RealWorld, a #)
- freeStablePtr# :: StablePtr# a -> State# RealWorld -> State# RealWorld
- deRefStablePtr# :: StablePtr# a -> State# RealWorld ->
- (# State# RealWorld, a #)
- \end{verbatim}
-
- There may be additional functions on the C side to allow evaluation,
- application, etc of a stable pointer.
-
- Stable Pointers are exported to the outside world as indices and not
- pointers, because the stable pointer table is allowed to be
- reallocated for growth. The table is never shrunk for its space to
- be reclaimed.
-
- Future plans for stable ptrs include distinguishing them by the
- generation of the pointed object. See
- http://ghc.haskell.org/trac/ghc/ticket/7670 for details.
-*/
-
snEntry *stable_name_table = NULL;
static snEntry *stable_name_free = NULL;
static unsigned int SNT_size = 0;
#define INIT_SNT_SIZE 64
-spEntry *stable_ptr_table = NULL;
-static spEntry *stable_ptr_free = NULL;
-static unsigned int SPT_size = 0;
-#define INIT_SPT_SIZE 64
-
-/* Each time the stable pointer table is enlarged, we temporarily retain the old
- * version to ensure dereferences are thread-safe (see Note [Enlarging the
- * stable pointer table]). Since we double the size of the table each time, we
- * can (theoretically) enlarge it at most N times on an N-bit machine. Thus,
- * there will never be more than N old versions of the table.
- */
-#if SIZEOF_VOID_P == 4
-#define MAX_N_OLD_SPTS 32
-#elif SIZEOF_VOID_P == 8
-#define MAX_N_OLD_SPTS 64
-#else
-#error unknown SIZEOF_VOID_P
-#endif
-
-static spEntry *old_SPTs[MAX_N_OLD_SPTS];
-static uint32_t n_old_SPTs = 0;
-
#if defined(THREADED_RTS)
-Mutex stable_mutex;
+Mutex stable_name_mutex;
#endif
static void enlargeStableNameTable(void);
-static void enlargeStablePtrTable(void);
/*
* This hash table maps Haskell objects to stable names, so that every
@@ -130,26 +38,21 @@ static void enlargeStablePtrTable(void);
static HashTable *addrToStableHash = NULL;
-/* -----------------------------------------------------------------------------
- * We must lock the StablePtr table during GC, to prevent simultaneous
- * calls to freeStablePtr().
- * -------------------------------------------------------------------------- */
-
void
-stableLock(void)
+stableNameLock(void)
{
- initStableTables();
- ACQUIRE_LOCK(&stable_mutex);
+ initStableNameTable();
+ ACQUIRE_LOCK(&stable_name_mutex);
}
void
-stableUnlock(void)
+stableNameUnlock(void)
{
- RELEASE_LOCK(&stable_mutex);
+ RELEASE_LOCK(&stable_name_mutex);
}
/* -----------------------------------------------------------------------------
- * Initialising the tables
+ * Initialising the table
* -------------------------------------------------------------------------- */
STATIC_INLINE void
@@ -165,19 +68,8 @@ initSnEntryFreeList(snEntry *table, uint32_t n, snEntry *free)
stable_name_free = table;
}
-STATIC_INLINE void
-initSpEntryFreeList(spEntry *table, uint32_t n, spEntry *free)
-{
- spEntry *p;
- for (p = table + n - 1; p >= table; p--) {
- p->addr = (P_)free;
- free = p;
- }
- stable_ptr_free = table;
-}
-
void
-initStableTables(void)
+initStableNameTable(void)
{
if (SNT_size > 0) return;
SNT_size = INIT_SNT_SIZE;
@@ -190,14 +82,8 @@ initStableTables(void)
initSnEntryFreeList(stable_name_table + 1,INIT_SNT_SIZE-1,NULL);
addrToStableHash = allocHashTable();
- if (SPT_size > 0) return;
- SPT_size = INIT_SPT_SIZE;
- stable_ptr_table = stgMallocBytes(SPT_size * sizeof(spEntry),
- "initStablePtrTable");
- initSpEntryFreeList(stable_ptr_table,INIT_SPT_SIZE,NULL);
-
#if defined(THREADED_RTS)
- initMutex(&stable_mutex);
+ initMutex(&stable_name_mutex);
#endif
}
@@ -220,37 +106,6 @@ enlargeStableNameTable(void)
initSnEntryFreeList(stable_name_table + old_SNT_size, old_SNT_size, NULL);
}
-// Must be holding stable_mutex
-static void
-enlargeStablePtrTable(void)
-{
- uint32_t old_SPT_size = SPT_size;
- spEntry *new_stable_ptr_table;
-
- // 2nd and subsequent times
- SPT_size *= 2;
-
- /* We temporarily retain the old version instead of freeing it; see Note
- * [Enlarging the stable pointer table].
- */
- new_stable_ptr_table =
- stgMallocBytes(SPT_size * sizeof(spEntry),
- "enlargeStablePtrTable");
- memcpy(new_stable_ptr_table,
- stable_ptr_table,
- old_SPT_size * sizeof(spEntry));
- ASSERT(n_old_SPTs < MAX_N_OLD_SPTS);
- old_SPTs[n_old_SPTs++] = stable_ptr_table;
-
- /* When using the threaded RTS, the update of stable_ptr_table is assumed to
- * be atomic, so that another thread simultaneously dereferencing a stable
- * pointer will always read a valid address.
- */
- stable_ptr_table = new_stable_ptr_table;
-
- initSpEntryFreeList(stable_ptr_table + old_SPT_size, old_SPT_size, NULL);
-}
-
/* Note [Enlarging the stable pointer table]
*
* To enlarge the stable pointer table, we allocate a new table, copy the
@@ -271,19 +126,8 @@ enlargeStablePtrTable(void)
* Freeing entries and tables
* -------------------------------------------------------------------------- */
-static void
-freeOldSPTs(void)
-{
- uint32_t i;
-
- for (i = 0; i < n_old_SPTs; i++) {
- stgFree(old_SPTs[i]);
- }
- n_old_SPTs = 0;
-}
-
void
-exitStableTables(void)
+exitStableNameTable(void)
{
if (addrToStableHash)
freeHashTable(addrToStableHash, NULL);
@@ -294,15 +138,8 @@ exitStableTables(void)
stable_name_table = NULL;
SNT_size = 0;
- if (stable_ptr_table)
- stgFree(stable_ptr_table);
- stable_ptr_table = NULL;
- SPT_size = 0;
-
- freeOldSPTs();
-
#if defined(THREADED_RTS)
- closeMutex(&stable_mutex);
+ closeMutex(&stable_name_mutex);
#endif
}
@@ -315,28 +152,6 @@ freeSnEntry(snEntry *sn)
stable_name_free = sn;
}
-STATIC_INLINE void
-freeSpEntry(spEntry *sp)
-{
- sp->addr = (P_)stable_ptr_free;
- stable_ptr_free = sp;
-}
-
-void
-freeStablePtrUnsafe(StgStablePtr sp)
-{
- ASSERT((StgWord)sp < SPT_size);
- freeSpEntry(&stable_ptr_table[(StgWord)sp]);
-}
-
-void
-freeStablePtr(StgStablePtr sp)
-{
- stableLock();
- freeStablePtrUnsafe(sp);
- stableUnlock();
-}
-
/* -----------------------------------------------------------------------------
* Looking up
* -------------------------------------------------------------------------- */
@@ -377,7 +192,7 @@ removeIndirections (StgClosure* p)
StgWord
lookupStableName (StgPtr p)
{
- stableLock();
+ stableNameLock();
if (stable_name_free == NULL) {
enlargeStableNameTable();
@@ -396,7 +211,7 @@ lookupStableName (StgPtr p)
if (sn != 0) {
ASSERT(stable_name_table[sn].addr == p);
debugTrace(DEBUG_stable, "cached stable name %ld at %p",sn,p);
- stableUnlock();
+ stableNameUnlock();
return sn;
}
@@ -409,44 +224,15 @@ lookupStableName (StgPtr p)
/* add the new stable name to the hash table */
insertHashTable(addrToStableHash, (W_)p, (void *)sn);
- stableUnlock();
+ stableNameUnlock();
return sn;
}
-StgStablePtr
-getStablePtr(StgPtr p)
-{
- StgWord sp;
-
- stableLock();
- if (!stable_ptr_free) enlargeStablePtrTable();
- sp = stable_ptr_free - stable_ptr_table;
- stable_ptr_free = (spEntry*)(stable_ptr_free->addr);
- stable_ptr_table[sp].addr = p;
- stableUnlock();
- return (StgStablePtr)(sp);
-}
-
/* -----------------------------------------------------------------------------
- * Treat stable pointers as roots for the garbage collector.
+ * Remember old stable name addresses
* -------------------------------------------------------------------------- */
-#define FOR_EACH_STABLE_PTR(p, CODE) \
- do { \
- spEntry *p; \
- spEntry *__end_ptr = &stable_ptr_table[SPT_size]; \
- for (p = stable_ptr_table; p < __end_ptr; p++) { \
- /* Internal pointers are free slots. NULL is last in free */ \
- /* list. */ \
- if (p->addr && \
- (p->addr < (P_)stable_ptr_table || p->addr >= (P_)__end_ptr)) \
- { \
- do { CODE } while(0); \
- } \
- } \
- } while(0)
-
#define FOR_EACH_STABLE_NAME(p, CODE) \
do { \
snEntry *p; \
@@ -468,31 +254,13 @@ getStablePtr(StgPtr p)
} \
} while(0)
-STATIC_INLINE void
-markStablePtrTable(evac_fn evac, void *user)
-{
- FOR_EACH_STABLE_PTR(p, evac(user, (StgClosure **)&p->addr););
-}
-
-STATIC_INLINE void
+void
rememberOldStableNameAddresses(void)
{
/* TODO: Only if !full GC */
FOR_EACH_STABLE_NAME(p, p->old = p->addr;);
}
-void
-markStableTables(evac_fn evac, void *user)
-{
- /* Since no other thread can currently be dereferencing a stable pointer, it
- * is safe to free the old versions of the table.
- */
- freeOldSPTs();
-
- markStablePtrTable(evac, user);
- rememberOldStableNameAddresses();
-}
-
/* -----------------------------------------------------------------------------
* Thread the stable pointer table for compacting GC.
*
@@ -501,7 +269,7 @@ markStableTables(evac_fn evac, void *user)
* collector may move the object it points to.
* -------------------------------------------------------------------------- */
-STATIC_INLINE void
+void
threadStableNameTable( evac_fn evac, void *user )
{
FOR_EACH_STABLE_NAME(p, {
@@ -514,19 +282,6 @@ threadStableNameTable( evac_fn evac, void *user )
});
}
-STATIC_INLINE void
-threadStablePtrTable( evac_fn evac, void *user )
-{
- FOR_EACH_STABLE_PTR(p, evac(user, (StgClosure **)&p->addr););
-}
-
-void
-threadStableTables( evac_fn evac, void *user )
-{
- threadStableNameTable(evac, user);
- threadStablePtrTable(evac, user);
-}
-
/* -----------------------------------------------------------------------------
* Garbage collect any dead entries in the stable name table.
*
@@ -542,7 +297,7 @@ threadStableTables( evac_fn evac, void *user )
* -------------------------------------------------------------------------- */
void
-gcStableTables( void )
+gcStableNameTable( void )
{
FOR_EACH_STABLE_NAME(
p, {
@@ -579,7 +334,7 @@ gcStableTables( void )
* -------------------------------------------------------------------------- */
void
-updateStableTables(bool full)
+updateStableNameTable(bool full)
{
if (full && addrToStableHash != NULL && 0 != keyCountHashTable(addrToStableHash)) {
freeHashTable(addrToStableHash,NULL);
diff --git a/rts/StableName.h b/rts/StableName.h
new file mode 100644
index 0000000000..6b5e551add
--- /dev/null
+++ b/rts/StableName.h
@@ -0,0 +1,31 @@
+/* -----------------------------------------------------------------------------
+ *
+ * (c) The GHC Team, 1998-2004
+ *
+ * ---------------------------------------------------------------------------*/
+
+#pragma once
+
+#include "sm/GC.h" // for evac_fn below
+
+#include "BeginPrivate.h"
+
+void initStableNameTable ( void );
+void exitStableNameTable ( void );
+StgWord lookupStableName ( StgPtr p );
+
+void rememberOldStableNameAddresses ( void );
+
+void threadStableNameTable ( evac_fn evac, void *user );
+void gcStableNameTable ( void );
+void updateStableNameTable ( bool full );
+
+void stableNameLock ( void );
+void stableNameUnlock ( void );
+
+#if defined(THREADED_RTS)
+// needed by Schedule.c:forkProcess()
+extern Mutex stable_name_mutex;
+#endif
+
+#include "EndPrivate.h"
diff --git a/rts/StablePtr.c b/rts/StablePtr.c
new file mode 100644
index 0000000000..0f53ffcdc4
--- /dev/null
+++ b/rts/StablePtr.c
@@ -0,0 +1,329 @@
+/* -*- tab-width: 4 -*- */
+
+/* -----------------------------------------------------------------------------
+ *
+ * (c) The GHC Team, 1998-2002
+ *
+ * Stable pointers
+ *
+ * ---------------------------------------------------------------------------*/
+
+#include "PosixSource.h"
+#include "Rts.h"
+#include "RtsAPI.h"
+
+#include "Hash.h"
+#include "RtsUtils.h"
+#include "Trace.h"
+#include "StablePtr.h"
+
+#include <string.h>
+
+/* Comment from ADR's implementation in old RTS:
+
+ This files (together with @ghc/runtime/storage/PerformIO.lhc@ and a
+ small change in @HpOverflow.lc@) consists of the changes in the
+ runtime system required to implement "Stable Pointers". But we're
+ getting a bit ahead of ourselves --- what is a stable pointer and what
+ is it used for?
+
+ When Haskell calls C, it normally just passes over primitive integers,
+ floats, bools, strings, etc. This doesn't cause any problems at all
+ for garbage collection because the act of passing them makes a copy
+ from the heap, stack or wherever they are onto the C-world stack.
+ However, if we were to pass a heap object such as a (Haskell) @String@
+ and a garbage collection occured before we finished using it, we'd run
+ into problems since the heap object might have been moved or even
+ deleted.
+
+ So, if a C call is able to cause a garbage collection or we want to
+ store a pointer to a heap object between C calls, we must be careful
+ when passing heap objects. Our solution is to keep a table of all
+ objects we've given to the C-world and to make sure that the garbage
+ collector collects these objects --- updating the table as required to
+ make sure we can still find the object.
+
+
+ Of course, all this rather begs the question: why would we want to
+ pass a boxed value?
+
+ One very good reason is to preserve laziness across the language
+ interface. Rather than evaluating an integer or a string because it
+ {\em might\/} be required by the C function, we can wait until the C
+ function actually wants the value and then force an evaluation.
+
+ Another very good reason (the motivating reason!) is that the C code
+ might want to execute an object of sort $IO ()$ for the side-effects
+ it will produce. For example, this is used when interfacing to an X
+ widgets library to allow a direct implementation of callbacks.
+
+ One final reason is that we may want to store composite Haskell
+ values in data structures implemented in the C side. Serializing and
+ deserializing these structures into unboxed form suitable for C may
+ be more expensive than maintaining the extra layer of indirection of
+ stable pointers.
+
+ The @makeStablePointer :: a -> IO (StablePtr a)@ function
+ converts a value into a stable pointer. It is part of the @PrimIO@
+ monad, because we want to be sure we don't allocate one twice by
+ accident, and then only free one of the copies.
+
+ \begin{verbatim}
+ makeStablePtr# :: a -> State# RealWorld -> (# RealWorld, a #)
+ freeStablePtr# :: StablePtr# a -> State# RealWorld -> State# RealWorld
+ deRefStablePtr# :: StablePtr# a -> State# RealWorld ->
+ (# State# RealWorld, a #)
+ \end{verbatim}
+
+ There may be additional functions on the C side to allow evaluation,
+ application, etc of a stable pointer.
+
+ Stable Pointers are exported to the outside world as indices and not
+ pointers, because the stable pointer table is allowed to be
+ reallocated for growth. The table is never shrunk for its space to
+ be reclaimed.
+
+ Future plans for stable ptrs include distinguishing them by the
+ generation of the pointed object. See
+ http://ghc.haskell.org/trac/ghc/ticket/7670 for details.
+*/
+
+spEntry *stable_ptr_table = NULL;
+static spEntry *stable_ptr_free = NULL;
+static unsigned int SPT_size = 0;
+#define INIT_SPT_SIZE 64
+
+/* Each time the stable pointer table is enlarged, we temporarily retain the old
+ * version to ensure dereferences are thread-safe (see Note [Enlarging the
+ * stable pointer table]). Since we double the size of the table each time, we
+ * can (theoretically) enlarge it at most N times on an N-bit machine. Thus,
+ * there will never be more than N old versions of the table.
+ */
+#if SIZEOF_VOID_P == 4
+#define MAX_N_OLD_SPTS 32
+#elif SIZEOF_VOID_P == 8
+#define MAX_N_OLD_SPTS 64
+#else
+#error unknown SIZEOF_VOID_P
+#endif
+
+static spEntry *old_SPTs[MAX_N_OLD_SPTS];
+static uint32_t n_old_SPTs = 0;
+
+#if defined(THREADED_RTS)
+Mutex stable_ptr_mutex;
+#endif
+
+static void enlargeStablePtrTable(void);
+
+/* -----------------------------------------------------------------------------
+ * We must lock the StablePtr table during GC, to prevent simultaneous
+ * calls to freeStablePtr().
+ * -------------------------------------------------------------------------- */
+
+void
+stablePtrLock(void)
+{
+ initStablePtrTable();
+ ACQUIRE_LOCK(&stable_ptr_mutex);
+}
+
+void
+stablePtrUnlock(void)
+{
+ RELEASE_LOCK(&stable_ptr_mutex);
+}
+
+/* -----------------------------------------------------------------------------
+ * Initialising the table
+ * -------------------------------------------------------------------------- */
+
+STATIC_INLINE void
+initSpEntryFreeList(spEntry *table, uint32_t n, spEntry *free)
+{
+ spEntry *p;
+ for (p = table + n - 1; p >= table; p--) {
+ p->addr = (P_)free;
+ free = p;
+ }
+ stable_ptr_free = table;
+}
+
+void
+initStablePtrTable(void)
+{
+ if (SPT_size > 0) return;
+ SPT_size = INIT_SPT_SIZE;
+ stable_ptr_table = stgMallocBytes(SPT_size * sizeof(spEntry),
+ "initStablePtrTable");
+ initSpEntryFreeList(stable_ptr_table,INIT_SPT_SIZE,NULL);
+
+#if defined(THREADED_RTS)
+ initMutex(&stable_ptr_mutex);
+#endif
+}
+
+/* -----------------------------------------------------------------------------
+ * Enlarging the table
+ * -------------------------------------------------------------------------- */
+
+// Must be holding stable_ptr_mutex
+static void
+enlargeStablePtrTable(void)
+{
+ uint32_t old_SPT_size = SPT_size;
+ spEntry *new_stable_ptr_table;
+
+ // 2nd and subsequent times
+ SPT_size *= 2;
+
+ /* We temporarily retain the old version instead of freeing it; see Note
+ * [Enlarging the stable pointer table].
+ */
+ new_stable_ptr_table =
+ stgMallocBytes(SPT_size * sizeof(spEntry),
+ "enlargeStablePtrTable");
+ memcpy(new_stable_ptr_table,
+ stable_ptr_table,
+ old_SPT_size * sizeof(spEntry));
+ ASSERT(n_old_SPTs < MAX_N_OLD_SPTS);
+ old_SPTs[n_old_SPTs++] = stable_ptr_table;
+
+ /* When using the threaded RTS, the update of stable_ptr_table is assumed to
+ * be atomic, so that another thread simultaneously dereferencing a stable
+ * pointer will always read a valid address.
+ */
+ stable_ptr_table = new_stable_ptr_table;
+
+ initSpEntryFreeList(stable_ptr_table + old_SPT_size, old_SPT_size, NULL);
+}
+
+/* Note [Enlarging the stable pointer table]
+ *
+ * To enlarge the stable pointer table, we allocate a new table, copy the
+ * existing entries, and then store the old version of the table in old_SPTs
+ * until we free it during GC. By not immediately freeing the old version
+ * (or equivalently by not growing the table using realloc()), we ensure that
+ * another thread simultaneously dereferencing a stable pointer using the old
+ * version can safely access the table without causing a segfault (see Trac
+ * #10296).
+ *
+ * Note that because the stable pointer table is doubled in size each time it is
+ * enlarged, the total memory needed to store the old versions is always less
+ * than that required to hold the current version.
+ */
+
+
+/* -----------------------------------------------------------------------------
+ * Freeing entries and tables
+ * -------------------------------------------------------------------------- */
+
+static void
+freeOldSPTs(void)
+{
+ uint32_t i;
+
+ for (i = 0; i < n_old_SPTs; i++) {
+ stgFree(old_SPTs[i]);
+ }
+ n_old_SPTs = 0;
+}
+
+void
+exitStablePtrTable(void)
+{
+ if (stable_ptr_table)
+ stgFree(stable_ptr_table);
+ stable_ptr_table = NULL;
+ SPT_size = 0;
+
+ freeOldSPTs();
+
+#if defined(THREADED_RTS)
+ closeMutex(&stable_ptr_mutex);
+#endif
+}
+
+STATIC_INLINE void
+freeSpEntry(spEntry *sp)
+{
+ sp->addr = (P_)stable_ptr_free;
+ stable_ptr_free = sp;
+}
+
+void
+freeStablePtrUnsafe(StgStablePtr sp)
+{
+ ASSERT((StgWord)sp < SPT_size);
+ freeSpEntry(&stable_ptr_table[(StgWord)sp]);
+}
+
+void
+freeStablePtr(StgStablePtr sp)
+{
+ stablePtrLock();
+ freeStablePtrUnsafe(sp);
+ stablePtrUnlock();
+}
+
+/* -----------------------------------------------------------------------------
+ * Looking up
+ * -------------------------------------------------------------------------- */
+
+StgStablePtr
+getStablePtr(StgPtr p)
+{
+ StgWord sp;
+
+ stablePtrLock();
+ if (!stable_ptr_free) enlargeStablePtrTable();
+ sp = stable_ptr_free - stable_ptr_table;
+ stable_ptr_free = (spEntry*)(stable_ptr_free->addr);
+ stable_ptr_table[sp].addr = p;
+ stablePtrUnlock();
+ return (StgStablePtr)(sp);
+}
+
+/* -----------------------------------------------------------------------------
+ * Treat stable pointers as roots for the garbage collector.
+ * -------------------------------------------------------------------------- */
+
+#define FOR_EACH_STABLE_PTR(p, CODE) \
+ do { \
+ spEntry *p; \
+ spEntry *__end_ptr = &stable_ptr_table[SPT_size]; \
+ for (p = stable_ptr_table; p < __end_ptr; p++) { \
+ /* Internal pointers are free slots. NULL is last in free */ \
+ /* list. */ \
+ if (p->addr && \
+ (p->addr < (P_)stable_ptr_table || p->addr >= (P_)__end_ptr)) \
+ { \
+ do { CODE } while(0); \
+ } \
+ } \
+ } while(0)
+
+void
+markStablePtrTable(evac_fn evac, void *user)
+{
+ /* Since no other thread can currently be dereferencing a stable pointer, it
+ * is safe to free the old versions of the table.
+ */
+ freeOldSPTs();
+
+ FOR_EACH_STABLE_PTR(p, evac(user, (StgClosure **)&p->addr););
+}
+
+/* -----------------------------------------------------------------------------
+ * Thread the stable pointer table for compacting GC.
+ *
+ * Here we must call the supplied evac function for each pointer into
+ * the heap from the stable tables, because the compacting
+ * collector may move the object it points to.
+ * -------------------------------------------------------------------------- */
+
+void
+threadStablePtrTable( evac_fn evac, void *user )
+{
+ FOR_EACH_STABLE_PTR(p, evac(user, (StgClosure **)&p->addr););
+}
diff --git a/rts/Stable.h b/rts/StablePtr.h
index 399a2b3877..3fb305b47b 100644
--- a/rts/Stable.h
+++ b/rts/StablePtr.h
@@ -20,32 +20,27 @@
void freeStablePtr ( StgStablePtr sp );
-/* Use the "Unsafe" one after manually locking with stableLock/stableUnlock */
+/* Use the "Unsafe" one after only when manually locking and
+ unlocking with stablePtrLock/stablePtrUnlock */
void freeStablePtrUnsafe ( StgStablePtr sp );
-void initStableTables ( void );
-void exitStableTables ( void );
-StgWord lookupStableName ( StgPtr p );
+void initStablePtrTable ( void );
+void exitStablePtrTable ( void );
-/* Call given function on every stable ptr. markStableTables depends
+/* Call given function on every stable ptr. markStablePtrTable depends
* on the function updating its pointers in case the object is
- * moved. */
-/* TODO: This also remembers old stable name addresses, which isn't
- * necessary in some contexts markStableTables is called from.
- * Consider splitting it.
+ * moved.
*/
-void markStableTables ( evac_fn evac, void *user );
+void markStablePtrTable ( evac_fn evac, void *user );
-void threadStableTables ( evac_fn evac, void *user );
-void gcStableTables ( void );
-void updateStableTables ( bool full );
+void threadStablePtrTable ( evac_fn evac, void *user );
-void stableLock ( void );
-void stableUnlock ( void );
+void stablePtrLock ( void );
+void stablePtrUnlock ( void );
#if defined(THREADED_RTS)
// needed by Schedule.c:forkProcess()
-extern Mutex stable_mutex;
+extern Mutex stable_ptr_mutex;
#endif
#include "EndPrivate.h"
diff --git a/rts/StaticPtrTable.c b/rts/StaticPtrTable.c
index 7711377d7f..0b2244025e 100644
--- a/rts/StaticPtrTable.c
+++ b/rts/StaticPtrTable.c
@@ -12,7 +12,7 @@
#include "Rts.h"
#include "RtsUtils.h"
#include "Hash.h"
-#include "Stable.h"
+#include "StablePtr.h"
static HashTable * spt = NULL;
diff --git a/rts/TopHandler.c b/rts/TopHandler.c
index 8e868e6e92..c0ac936b85 100644
--- a/rts/TopHandler.c
+++ b/rts/TopHandler.c
@@ -1,5 +1,5 @@
#include "Rts.h"
-#include "Stable.h"
+#include "StablePtr.h"
#include "TopHandler.h"
#if defined(THREADED_RTS)
diff --git a/rts/TopHandler.h b/rts/TopHandler.h
index 1146eea71c..d724354d9a 100644
--- a/rts/TopHandler.h
+++ b/rts/TopHandler.h
@@ -13,7 +13,6 @@
#include <rts/Types.h>
#include <rts/storage/Closures.h>
#include <stg/Types.h>
-#include <rts/Stable.h>
// Initialize the top handler subsystem
void initTopHandler(void);
diff --git a/rts/posix/Signals.c b/rts/posix/Signals.c
index 4cd1e386cc..f033870d16 100644
--- a/rts/posix/Signals.c
+++ b/rts/posix/Signals.c
@@ -15,7 +15,6 @@
#include "RtsUtils.h"
#include "Prelude.h"
#include "Ticker.h"
-#include "Stable.h"
#include "ThreadLabels.h"
#include "Libdw.h"
diff --git a/rts/rts.cabal.in b/rts/rts.cabal.in
index d41135ddd3..d509953a1b 100644
--- a/rts/rts.cabal.in
+++ b/rts/rts.cabal.in
@@ -127,7 +127,8 @@ library
rts/Profiling.h
rts/Signals.h
rts/SpinLock.h
- rts/Stable.h
+ rts/StableName.h
+ rts/StablePtr.h
rts/StaticPtrTable.h
rts/TTY.h
rts/Threads.h
@@ -393,7 +394,8 @@ library
STM.c
Schedule.c
Sparks.c
- Stable.c
+ StableName.c
+ StablePtr.c
StaticPtrTable.c
Stats.c
StgCRun.c
diff --git a/rts/sm/Compact.c b/rts/sm/Compact.c
index 10ad73c7d7..004e042069 100644
--- a/rts/sm/Compact.c
+++ b/rts/sm/Compact.c
@@ -25,7 +25,8 @@
#include "Trace.h"
#include "Weak.h"
#include "MarkWeak.h"
-#include "Stable.h"
+#include "StablePtr.h"
+#include "StableName.h"
// Turn off inlining when debugging - it obfuscates things
#if defined(DEBUG)
@@ -1000,7 +1001,10 @@ compact(StgClosure *static_objects)
thread_static(static_objects /* ToDo: ok? */);
// the stable pointer table
- threadStableTables((evac_fn)thread_root, NULL);
+ threadStablePtrTable((evac_fn)thread_root, NULL);
+
+ // the stable name table
+ threadStableNameTable((evac_fn)thread_root, NULL);
// the CAF list (used by GHCi)
markCAFs((evac_fn)thread_root, NULL);
diff --git a/rts/sm/GC.c b/rts/sm/GC.c
index 90857abe38..70d6d8efe5 100644
--- a/rts/sm/GC.c
+++ b/rts/sm/GC.c
@@ -46,7 +46,8 @@
#include "RetainerProfile.h"
#include "LdvProfile.h"
#include "RaiseAsync.h"
-#include "Stable.h"
+#include "StableName.h"
+#include "StablePtr.h"
#include "CheckUnload.h"
#include "CNF.h"
#include "RtsFlags.h"
@@ -238,8 +239,9 @@ GarbageCollect (uint32_t collect_gen,
// tell the stats department that we've started a GC
stat_startGC(cap, gct);
- // lock the StablePtr table
- stableLock();
+ // Lock the StablePtr table. This prevents FFI calls manipulating
+ // the table from occurring during GC.
+ stablePtrLock();
#if defined(DEBUG)
mutlist_MUTVARS = 0;
@@ -405,7 +407,10 @@ GarbageCollect (uint32_t collect_gen,
initWeakForGC();
// Mark the stable pointer table.
- markStableTables(mark_root, gct);
+ markStablePtrTable(mark_root, gct);
+
+ // Remember old stable name addresses.
+ rememberOldStableNameAddresses ();
/* -------------------------------------------------------------------------
* Repeatedly scavenge all the areas we know about until there's no
@@ -431,7 +436,7 @@ GarbageCollect (uint32_t collect_gen,
shutdown_gc_threads(gct->thread_index, idle_cap);
// Now see which stable names are still alive.
- gcStableTables();
+ gcStableNameTable();
#if defined(THREADED_RTS)
if (n_gc_threads == 1) {
@@ -730,15 +735,15 @@ GarbageCollect (uint32_t collect_gen,
if (major_gc) { gcCAFs(); }
#endif
- // Update the stable pointer hash table.
- updateStableTables(major_gc);
+ // Update the stable name hash table
+ updateStableNameTable(major_gc);
// unlock the StablePtr table. Must be before scheduleFinalizers(),
// because a finalizer may call hs_free_fun_ptr() or
// hs_free_stable_ptr(), both of which access the StablePtr table.
- stableUnlock();
+ stablePtrUnlock();
- // Must be after stableUnlock(), because it might free stable ptrs.
+ // Must be after stablePtrUnlock(), because it might free stable ptrs.
if (major_gc) {
checkUnload (gct->scavenged_static_objects);
}
diff --git a/utils/deriveConstants/Main.hs b/utils/deriveConstants/Main.hs
index 84de914e08..5d5dbc404f 100644
--- a/utils/deriveConstants/Main.hs
+++ b/utils/deriveConstants/Main.hs
@@ -724,7 +724,7 @@ getWanted verbose os tmpdir gccProgram gccFlags nmProgram mobjdumpProgram
"",
"#include \"PosixSource.h\"",
"#include \"Rts.h\"",
- "#include \"Stable.h\"",
+ "#include \"StableName.h\"",
"#include \"Capability.h\"",
"",
"#include <inttypes.h>",