Commit f48e276a authored by David Feuer's avatar David Feuer Committed by David Feuer

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
parent 65eec9cf
......@@ -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.
......
......@@ -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
~~~~~~~~~~~~~~~~
......
......@@ -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
......
......@@ -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);
......
......@@ -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"
......
......@@ -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;
}
/* -----------------------------------------------------------------------------
*
* (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;
}
......@@ -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
......
......@@ -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"
......
......@@ -21,7 +21,7 @@
#include "Rts.h"
#include "Globals.h"
#include "Stable.h"
#include "StablePtr.h"
typedef enum {
GHCConcSignalSignalHandlerStore,
......
......@@ -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
......
......@@ -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"
......
......@@ -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"
......
......@@ -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
......
......@@ -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"
......
......@@ -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 */
......
......@@ -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) \
......
......@@ -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++) {
......
This diff is collapsed.
/* -----------------------------------------------------------------------------
*
* (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"
This diff is collapsed.
......@@ -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"
......@@ -12,7 +12,7 @@
#include "Rts.h"
#include "RtsUtils.h"
#include "Hash.h"
#include "Stable.h"
#include "StablePtr.h"
static HashTable * spt = NULL;
......
#include "Rts.h"
#include "Stable.h"
#include "StablePtr.h"
#include "TopHandler.h"
#if defined(THREADED_RTS)
......
......@@ -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);
......
......@@ -15,7 +15,6 @@
#include "RtsUtils.h"
#include "Prelude.h"
#include "Ticker.h"
#include "Stable.h"
#include "ThreadLabels.h"
#include "Libdw.h"
......
......@@ -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
......
......@@ -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);
......
......@@ -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);
}
......
......@@ -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>",
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment