Author: Armin Rigo <ar...@tunes.org> Branch: c8-locking Changeset: r1784:f0d995d5609d Date: 2015-06-01 16:47 +0200 http://bitbucket.org/pypy/stmgc/changeset/f0d995d5609d/
Log: Turn 'modification_lock' into one real POSIX read-write lock per segment. diff --git a/c8/LOCKS b/c8/LOCKS --- a/c8/LOCKS +++ b/c8/LOCKS @@ -41,6 +41,9 @@ any more. +--- UPDATE: modification_lock is now done with pthread_rwlock_xxx(). + + privatization_lock ================== diff --git a/c8/stm/core.c b/c8/stm/core.c --- a/c8/stm/core.c +++ b/c8/stm/core.c @@ -50,8 +50,8 @@ char *src_segment_base = (from_segnum >= 0 ? get_segment_base(from_segnum) : NULL); - assert(IMPLY(from_segnum >= 0, get_priv_segment(from_segnum)->modification_lock)); - assert(STM_PSEGMENT->modification_lock); + assert(IMPLY(from_segnum >= 0, modification_lock_check_rdlock(from_segnum))); + assert(modification_lock_check_wrlock(STM_SEGMENT->segment_num)); long my_segnum = STM_SEGMENT->segment_num; DEBUG_EXPECT_SEGFAULT(false); @@ -131,7 +131,7 @@ struct stm_commit_log_entry_s *from, struct stm_commit_log_entry_s *to) { - assert(STM_PSEGMENT->modification_lock); + assert(modification_lock_check_wrlock(STM_SEGMENT->segment_num)); assert(from->rev_num >= to->rev_num); /* walk BACKWARDS the commit log and update the page 'pagenum', initially at revision 'from', until we reach the revision 'to'. */ @@ -199,8 +199,8 @@ /* before copying anything, acquire modification locks from our and the other segment */ - uint64_t to_lock = (1UL << copy_from_segnum)| (1UL << my_segnum); - acquire_modification_lock_set(to_lock); + uint64_t to_lock = (1UL << copy_from_segnum); + acquire_modification_lock_set(to_lock, my_segnum); pagecopy(get_virtual_page(my_segnum, pagenum), get_virtual_page(copy_from_segnum, pagenum)); @@ -223,7 +223,7 @@ if (src_version->rev_num > target_version->rev_num) go_to_the_past(pagenum, src_version, target_version); - release_modification_lock_set(to_lock); + release_modification_lock_set(to_lock, my_segnum); release_all_privatization_locks(); } @@ -357,7 +357,7 @@ } /* Find the set of segments we need to copy from and lock them: */ - uint64_t segments_to_lock = 1UL << my_segnum; + uint64_t segments_to_lock = 0; cl = first_cl; while ((next_cl = cl->next) != NULL) { if (next_cl == INEV_RUNNING) { @@ -375,8 +375,8 @@ /* HERE */ - acquire_privatization_lock(STM_SEGMENT->segment_num); - acquire_modification_lock_set(segments_to_lock); + acquire_privatization_lock(my_segnum); + acquire_modification_lock_set(segments_to_lock, my_segnum); /* import objects from first_cl to last_cl: */ @@ -466,8 +466,8 @@ } /* done with modifications */ - release_modification_lock_set(segments_to_lock); - release_privatization_lock(STM_SEGMENT->segment_num); + release_modification_lock_set(segments_to_lock, my_segnum); + release_privatization_lock(my_segnum); } return !needs_abort; @@ -545,7 +545,7 @@ time" as the attach to commit log. Otherwise, another thread may see the new CL entry, import it, look for backup copies in this segment and find the old backup copies! */ - acquire_modification_lock(STM_SEGMENT->segment_num); + acquire_modification_lock_wr(STM_SEGMENT->segment_num); } /* try to attach to commit log: */ @@ -559,7 +559,7 @@ } if (is_commit) { - release_modification_lock(STM_SEGMENT->segment_num); + release_modification_lock_wr(STM_SEGMENT->segment_num); /* XXX: unfortunately, if we failed to attach our CL entry, we have to re-add the WB_EXECUTED flags before we try to validate again because of said condition (s.a) */ @@ -596,7 +596,7 @@ list_clear(STM_PSEGMENT->modified_old_objects); STM_PSEGMENT->last_commit_log_entry = new; - release_modification_lock(STM_SEGMENT->segment_num); + release_modification_lock_wr(STM_SEGMENT->segment_num); } } @@ -692,7 +692,7 @@ increment_total_allocated(slice_sz); memcpy(bk_slice, realobj + slice_off, slice_sz); - acquire_modification_lock(STM_SEGMENT->segment_num); + acquire_modification_lock_wr(STM_SEGMENT->segment_num); /* !! follows layout of "struct stm_undo_s" !! */ STM_PSEGMENT->modified_old_objects = list_append3( STM_PSEGMENT->modified_old_objects, @@ -700,7 +700,7 @@ (uintptr_t)bk_slice, /* bk_addr */ NEW_SLICE(slice_off, slice_sz)); dprintf(("> append slice %p, off=%lu, sz=%lu\n", bk_slice, slice_off, slice_sz)); - release_modification_lock(STM_SEGMENT->segment_num); + release_modification_lock_wr(STM_SEGMENT->segment_num); slice_off += slice_sz; } @@ -1347,7 +1347,7 @@ #pragma push_macro("STM_SEGMENT") #undef STM_PSEGMENT #undef STM_SEGMENT - assert(get_priv_segment(segment_num)->modification_lock); + assert(modification_lock_check_wrlock(segment_num)); struct stm_priv_segment_info_s *pseg = get_priv_segment(segment_num); struct list_s *list = pseg->modified_old_objects; @@ -1409,9 +1409,9 @@ _reset_object_cards(pseg, item, CARD_CLEAR, false, false); }); - acquire_modification_lock(segment_num); + acquire_modification_lock_wr(segment_num); reset_modified_from_backup_copies(segment_num); - release_modification_lock(segment_num); + release_modification_lock_wr(segment_num); _verify_cards_cleared_in_all_lists(pseg); stm_thread_local_t *tl = pseg->pub.running_thread; diff --git a/c8/stm/core.h b/c8/stm/core.h --- a/c8/stm/core.h +++ b/c8/stm/core.h @@ -74,11 +74,6 @@ struct stm_priv_segment_info_s { struct stm_segment_info_s pub; - /* lock protecting from concurrent modification of - 'modified_old_objects', page-revision-changes, ... - Always acquired in global order of segments to avoid deadlocks. */ - uint8_t modification_lock; - /* All the old objects (older than the current transaction) that the current transaction attempts to modify. This is used to track the STM status: these are old objects that where written @@ -359,53 +354,3 @@ release_privatization_lock(l); } } - - - -/* Modification locks are used to prevent copying from a segment - where either the revision of some pages is inconsistent with the - rest, or the modified_old_objects list is being modified (bk_copys). - - Lock ordering: acquire privatization lock around acquiring a set - of modification locks! -*/ - -static inline void acquire_modification_lock(int segnum) -{ - spinlock_acquire(get_priv_segment(segnum)->modification_lock); -} - -static inline void release_modification_lock(int segnum) -{ - spinlock_release(get_priv_segment(segnum)->modification_lock); -} - -static inline void acquire_modification_lock_set(uint64_t seg_set) -{ - assert(NB_SEGMENTS <= 64); - OPT_ASSERT(seg_set < (1 << NB_SEGMENTS)); - - /* acquire locks in global order */ - int i; - for (i = 0; i < NB_SEGMENTS; i++) { - if ((seg_set & (1 << i)) == 0) - continue; - - spinlock_acquire(get_priv_segment(i)->modification_lock); - } -} - -static inline void release_modification_lock_set(uint64_t seg_set) -{ - assert(NB_SEGMENTS <= 64); - OPT_ASSERT(seg_set < (1 << NB_SEGMENTS)); - - int i; - for (i = 0; i < NB_SEGMENTS; i++) { - if ((seg_set & (1 << i)) == 0) - continue; - - assert(get_priv_segment(i)->modification_lock); - spinlock_release(get_priv_segment(i)->modification_lock); - } -} diff --git a/c8/stm/forksupport.c b/c8/stm/forksupport.c --- a/c8/stm/forksupport.c +++ b/c8/stm/forksupport.c @@ -120,6 +120,9 @@ just release these locks early */ s_mutex_unlock(); + /* Re-init these locks; might be needed after a fork() */ + setup_modification_locks(); + /* Unregister all other stm_thread_local_t, mostly as a way to free the memory used by the shadowstacks diff --git a/c8/stm/locks.h b/c8/stm/locks.h new file mode 100644 --- /dev/null +++ b/c8/stm/locks.h @@ -0,0 +1,124 @@ + +/* Modification locks protect from concurrent modification of + 'modified_old_objects', page-revision-changes, ... + + Modification locks are used to prevent copying from a segment + where either the revision of some pages is inconsistent with the + rest, or the modified_old_objects list is being modified (bk_copys). + + Lock ordering: acquire privatization lock around acquiring a set + of modification locks! +*/ + +typedef struct { + pthread_rwlock_t lock; +#ifndef NDEBUG + volatile bool write_locked; +#endif +} modification_lock_t __attribute__((aligned(64))); + +static modification_lock_t _modlocks[NB_SEGMENTS - 1]; + + +static void setup_modification_locks(void) +{ + int i; + for (i = 1; i < NB_SEGMENTS; i++) { + if (pthread_rwlock_init(&_modlocks[i - 1].lock, NULL) != 0) + stm_fatalerror("pthread_rwlock_init: %m"); + } +} + +static void teardown_modification_locks(void) +{ + int i; + for (i = 1; i < NB_SEGMENTS; i++) + pthread_rwlock_destroy(&_modlocks[i - 1].lock); + memset(_modlocks, 0, sizeof(_modlocks)); +} + + +static inline void acquire_modification_lock_wr(int segnum) +{ + if (UNLIKELY(pthread_rwlock_wrlock(&_modlocks[segnum - 1].lock) != 0)) + stm_fatalerror("pthread_rwlock_wrlock: %m"); +#ifndef NDEBUG + assert(!_modlocks[segnum - 1].write_locked); + _modlocks[segnum - 1].write_locked = true; +#endif +} + +static inline void release_modification_lock_wr(int segnum) +{ +#ifndef NDEBUG + assert(_modlocks[segnum - 1].write_locked); + _modlocks[segnum - 1].write_locked = false; +#endif + if (UNLIKELY(pthread_rwlock_unlock(&_modlocks[segnum - 1].lock) != 0)) + stm_fatalerror("pthread_rwlock_unlock(wr): %m"); +} + +static void acquire_modification_lock_set(uint64_t readset, int write) +{ + /* acquire the modification lock in 'read' mode for all segments + in 'readset', plus the modification lock in 'write' mode for + the segment number 'write'. + */ + assert(NB_SEGMENTS <= 64); + OPT_ASSERT(readset < (1 << NB_SEGMENTS)); + assert((readset & 1) == 0); /* segment numbers normally start at 1 */ + assert(0 <= write && write < NB_SEGMENTS); /* use 0 to mean "nobody" */ + + /* acquire locks in global order */ + readset |= (1UL << write); + int i; + for (i = 1; i < NB_SEGMENTS; i++) { + if ((readset & (1UL << i)) == 0) + continue; + if (i == write) { + acquire_modification_lock_wr(write); + } + else { + if (UNLIKELY(pthread_rwlock_rdlock(&_modlocks[i - 1].lock) != 0)) + stm_fatalerror("pthread_rwlock_rdlock: %m"); + } + } +} + +static void release_modification_lock_set(uint64_t readset, int write) +{ + assert(NB_SEGMENTS <= 64); + OPT_ASSERT(readset < (1 << NB_SEGMENTS)); + + /* release lock order does not matter; prefer early release of + the write lock */ + if (write > 0) { + release_modification_lock_wr(write); + readset &= ~(1UL << write); + } + int i; + for (i = 1; i < NB_SEGMENTS; i++) { + if ((readset & (1UL << i)) == 0) + continue; + if (UNLIKELY(pthread_rwlock_unlock(&_modlocks[i - 1].lock) != 0)) + stm_fatalerror("pthread_rwlock_unlock(rd): %m"); + } +} + +#ifndef NDEBUG +static bool modification_lock_check_rdlock(int segnum) +{ + assert(segnum > 0); + if (_modlocks[segnum - 1].write_locked) + return false; + if (pthread_rwlock_trywrlock(&_modlocks[segnum - 1].lock) == 0) { + pthread_rwlock_unlock(&_modlocks[segnum - 1].lock); + return false; + } + return true; +} +static bool modification_lock_check_wrlock(int segnum) +{ + return segnum == 0 || _modlocks[segnum - 1].write_locked; +} +#endif diff --git a/c8/stm/setup.c b/c8/stm/setup.c --- a/c8/stm/setup.c +++ b/c8/stm/setup.c @@ -127,6 +127,7 @@ private range of addresses. */ + setup_modification_locks(); setup_sync(); setup_nursery(); setup_gcpage(); @@ -174,6 +175,7 @@ teardown_gcpage(); teardown_smallmalloc(); teardown_pages(); + teardown_modification_locks(); } static void _shadowstack_trap_page(char *start, int prot) diff --git a/c8/stmgc.c b/c8/stmgc.c --- a/c8/stmgc.c +++ b/c8/stmgc.c @@ -17,6 +17,7 @@ #include "stm/marker.h" #include "stm/rewind_setjmp.h" #include "stm/finalizer.h" +#include "stm/locks.h" #include "stm/misc.c" #include "stm/list.c" _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit