Changeset 20351 in webkit for trunk/JavaScriptCore/kjs


Ignore:
Timestamp:
Mar 20, 2007, 4:57:01 PM (18 years ago)
Author:
mjs
Message:

Reviewed by Geoff and Adam.



  • fixed a threadsafety bug discovered by testing this


  • enhanced threadsafety assertions in collector
  • API/JSCallbackObject.cpp: (KJS::JSCallbackObject::~JSCallbackObject): This destructor can't DropAllLocks around the finalize callback, because it gets called from garbage collection and we can't let other threads collect!
  • JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.vcproj:
  • kjs/JSLock.cpp: (KJS::JSLock::currentThreadIsHoldingLock): Added new function to allow stronger assertions than just that the lock is held by some thread (you can now assert that the current thread is holding it, given the new JSLock design).
  • kjs/JSLock.h:
  • kjs/collector.cpp: Refactored for portability plus added some stronger assertions. (KJS::Collector::allocate): (KJS::currentThreadStackBase): (KJS::Collector::registerAsMainThread): (KJS::onMainThread): (KJS::PlatformThread::PlatformThread): (KJS::getCurrentPlatformThread): (KJS::Collector::Thread::Thread): (KJS::destroyRegisteredThread): (KJS::Collector::registerThread): (KJS::Collector::markCurrentThreadConservatively): (KJS::suspendThread): (KJS::resumeThread): (KJS::getPlatformThreadRegisters): (KJS::otherThreadStackPointer): (KJS::otherThreadStackBase): (KJS::Collector::markOtherThreadConservatively): (KJS::Collector::markStackObjectsConservatively): (KJS::Collector::protect): (KJS::Collector::unprotect): (KJS::Collector::collectOnMainThreadOnly): (KJS::Collector::markMainThreadOnlyObjects): (KJS::Collector::collect):
  • kjs/collector.h:
  • wtf/FastMalloc.cpp: (WTF::fastMallocSetIsMultiThreaded):
  • wtf/FastMallocInternal.h:
  • wtf/Platform.h:
Location:
trunk/JavaScriptCore/kjs
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/JavaScriptCore/kjs/JSLock.cpp

    r20115 r20351  
    2525
    2626#include "collector.h"
     27#if USE(MULTIPLE_THREADS)
     28#include <pthread.h>
     29#endif
    2730
    2831namespace KJS {
     
    6669        pthread_mutex_unlock(&JSMutex);
    6770    }
     71}
     72
     73bool JSLock::currentThreadIsHoldingLock()
     74{
     75    pthread_once(&createDidLockJSMutexOnce, createDidLockJSMutex);
     76    return !!pthread_getspecific(didLockJSMutex);
    6877}
    6978
  • trunk/JavaScriptCore/kjs/JSLock.h

    r20115 r20351  
    6363        static void unlock();
    6464        static int lockCount();
     65        static bool currentThreadIsHoldingLock();
    6566
    6667        static void registerThread();
  • trunk/JavaScriptCore/kjs/collector.cpp

    r20115 r20351  
    3333#include <algorithm>
    3434
    35 #if PLATFORM(DARWIN)
    36 
    3735#include <pthread.h>
     36
     37#if PLATFORM(DARWIN)
     38
    3839#include <mach/mach_port.h>
    3940#include <mach/task.h>
     
    4546
    4647#elif PLATFORM(UNIX)
    47 
    48 #include <pthread.h>
    4948
    5049#if HAVE(PTHREAD_NP_H)
     
    113112
    114113#ifndef NDEBUG
     114
    115115class GCLock {
    116116    static bool isLocked;
     
    135135void* Collector::allocate(size_t s)
    136136{
    137   assert(JSLock::lockCount() > 0);
     137  ASSERT(JSLock::lockCount() > 0);
     138  ASSERT(JSLock::currentThreadIsHoldingLock());
    138139
    139140  // collect if needed
     
    180181    targetBlock = heap.blocks[i];
    181182    targetBlockUsedCells = targetBlock->usedCells;
    182     assert(targetBlockUsedCells <= CELLS_PER_BLOCK);
     183    ASSERT(targetBlockUsedCells <= CELLS_PER_BLOCK);
    183184    while (targetBlockUsedCells == CELLS_PER_BLOCK) {
    184185      if (++i == usedBlocks)
     
    186187      targetBlock = heap.blocks[i];
    187188      targetBlockUsedCells = targetBlock->usedCells;
    188       assert(targetBlockUsedCells <= CELLS_PER_BLOCK);
     189      ASSERT(targetBlockUsedCells <= CELLS_PER_BLOCK);
    189190    }
    190191    heap.firstBlockWithPossibleSpace = i;
     
    220221}
    221222
     223static inline void* currentThreadStackBase()
     224{
     225#if PLATFORM(DARWIN)
     226    pthread_t thread = pthread_self();
     227    return pthread_get_stackaddr_np(thread);
     228#elif PLATFORM(WIN_OS) && PLATFORM(X86) && COMPILER(MSVC)
     229    // offset 0x18 from the FS segment register gives a pointer to
     230    // the thread information block for the current thread
     231    NT_TIB* pTib;
     232    __asm {
     233        MOV EAX, FS:[18h]
     234        MOV pTib, EAX
     235    }
     236    return (void*)pTib->StackBase;
     237#elif PLATFORM(UNIX)
     238    static void *stackBase = 0;
     239    static pthread_t stackThread;
     240    pthread_t thread = pthread_self();
     241    if (stackBase == 0 || thread != stackThread) {
     242        pthread_attr_t sattr;
     243#if HAVE(PTHREAD_NP_H)
     244        // e.g. on FreeBSD 5.4, [email protected]
     245        pthread_attr_get_np(thread, &sattr);
     246#else
     247        // FIXME: this function is non-portable; other POSIX systems may have different np alternatives
     248        pthread_getattr_np(thread, &sattr);
     249#endif
     250        size_t stackSize;
     251        int rc = pthread_attr_getstack(&sattr, &stackBase, &stackSize);
     252        (void)rc; // FIXME: deal with error code somehow?  seems fatal...
     253        ASSERT(stackBase);
     254        return (void*)(size_t(stackBase) + stackSize);
     255        stackThread = thread;
     256    }
     257#else
     258#error Need a way to get the stack base on this platform
     259#endif
     260}
     261
     262static pthread_t mainThread;
     263
     264void Collector::registerAsMainThread()
     265{
     266    mainThread = pthread_self();
     267}
     268
     269static inline bool onMainThread()
     270{
     271#if PLATFORM(DARWIN)
     272    return pthread_main_np();
     273#else
     274    return !!pthread_equal(pthread_self(), mainThread);
     275#endif
     276}
     277
    222278#if USE(MULTIPLE_THREADS)
    223279
    224 struct Collector::Thread {
    225   Thread(pthread_t pthread, mach_port_t mthread) : posixThread(pthread), machThread(mthread) {}
    226   Thread *next;
     280#if PLATFORM(DARWIN)
     281typedef mach_port_t PlatformThread;
     282#elif PLATFORM(WIN_OS)
     283struct PlatformThread {
     284    PlatformThread(DWORD _id, HANDLE _handle) : id(_id), handle(_handle) {}
     285    DWORD id;
     286    HANDLE handle;
     287};
     288#endif
     289
     290static inline PlatformThread getCurrentPlatformThread()
     291{
     292#if PLATFORM(DARWIN)
     293    return pthread_mach_thread_np(pthread_self());
     294#elif PLATFORM(WIN_OS)
     295    HANDLE threadHandle = pthread_getw32threadhandle_np(pthread_self());
     296    return PlatformThread(GetCurrentThreadId(), threadHandle);
     297#endif
     298}
     299
     300class Collector::Thread {
     301public:
     302  Thread(pthread_t pthread, const PlatformThread& platThread) : posixThread(pthread), platformThread(platThread) {}
     303  Thread* next;
    227304  pthread_t posixThread;
    228   mach_port_t machThread;
     305  PlatformThread platformThread;
    229306};
    230307
    231308pthread_key_t registeredThreadKey;
    232309pthread_once_t registeredThreadKeyOnce = PTHREAD_ONCE_INIT;
    233 Collector::Thread *registeredThreads;
    234  
    235 static void destroyRegisteredThread(void *data)
    236 {
    237   Collector::Thread *thread = (Collector::Thread *)data;
     310Collector::Thread* registeredThreads;
     311
     312static void destroyRegisteredThread(void* data)
     313{
     314  Collector::Thread* thread = (Collector::Thread*)data;
    238315
    239316  // Can't use JSLock convenience object here because we don't want to re-register
     
    247324    Collector::Thread *t;
    248325    for (t = registeredThreads->next; t != NULL; t = t->next) {
    249       if (t == thread) {
    250         last->next = t->next;
     326      if (t == thread) {         
     327          last->next = t->next;
    251328          break;
    252329      }
     
    269346{
    270347  ASSERT(JSLock::lockCount() > 0);
     348  ASSERT(JSLock::currentThreadIsHoldingLock());
    271349 
    272350  pthread_once(&registeredThreadKeyOnce, initializeRegisteredThreadKey);
    273351
    274352  if (!pthread_getspecific(registeredThreadKey)) {
    275     if (!pthread_main_np())
     353    if (!onMainThread())
    276354        WTF::fastMallocSetIsMultiThreaded();
    277     pthread_t pthread = pthread_self();
    278     Collector::Thread *thread = new Collector::Thread(pthread, pthread_mach_thread_np(pthread));
     355    Collector::Thread *thread = new Collector::Thread(pthread_self(), getCurrentPlatformThread());
     356
    279357    thread->next = registeredThreads;
    280358    registeredThreads = thread;
     
    298376  }
    299377
    300   assert(((char *)end - (char *)start) < 0x1000000);
    301   assert(IS_POINTER_ALIGNED(start));
    302   assert(IS_POINTER_ALIGNED(end));
     378  ASSERT(((char *)end - (char *)start) < 0x1000000);
     379  ASSERT(IS_POINTER_ALIGNED(start));
     380  ASSERT(IS_POINTER_ALIGNED(end));
    303381 
    304382  char **p = (char **)start;
     
    337415void Collector::markCurrentThreadConservatively()
    338416{
     417    // setjmp forces volatile registers onto the stack
    339418    jmp_buf registers;
    340419#if COMPILER(MSVC)
     
    347426#endif
    348427
    349 #if PLATFORM(DARWIN)
    350     pthread_t thread = pthread_self();
    351     void *stackBase = pthread_get_stackaddr_np(thread);
    352 #elif PLATFORM(WIN_OS) && PLATFORM(X86) && COMPILER(MSVC)
    353     NT_TIB *pTib;
    354     __asm {
    355         MOV EAX, FS:[18h]
    356         MOV pTib, EAX
    357     }
    358     void *stackBase = (void *)pTib->StackBase;
    359 #elif PLATFORM(UNIX)
    360     static void *stackBase = 0;
    361     static pthread_t stackThread;
    362     pthread_t thread = pthread_self();
    363     if (stackBase == 0 || thread != stackThread) {
    364         pthread_attr_t sattr;
    365 #if HAVE(PTHREAD_NP_H)
    366         // e.g. on FreeBSD 5.4, [email protected]
    367         pthread_attr_get_np(thread, &sattr);
    368 #else
    369         // FIXME: this function is non-portable; other POSIX systems may have different np alternatives
    370         pthread_getattr_np(thread, &sattr);
    371 #endif
    372         size_t stackSize;
    373         int rc = pthread_attr_getstack(&sattr, &stackBase, &stackSize);
    374         (void)rc; // FIXME: deal with error code somehow?  seems fatal...
    375         assert(stackBase);
    376         stackBase = (void*)(size_t(stackBase) + stackSize);
    377         stackThread = thread;
    378     }
    379 #else
    380 #error Need a way to get the stack base on this platform
    381 #endif
    382 
    383     void *dummy;
    384     void *stackPointer = &dummy;
     428    void* dummy;
     429    void* stackPointer = &dummy;
     430    void* stackBase = currentThreadStackBase();
    385431
    386432    markStackObjectsConservatively(stackPointer, stackBase);
     
    389435#if USE(MULTIPLE_THREADS)
    390436
     437static inline void suspendThread(const PlatformThread& platformThread)
     438{
     439#if PLATFORM(DARWIN)
     440  thread_suspend(platformThread);
     441#elif PLATFORM(WIN_OS)
     442  SuspendThread(platformThread.handle);
     443#else
     444#error Need a way to suspend threads on this platform
     445#endif
     446}
     447
     448static inline void resumeThread(const PlatformThread& platformThread)
     449{
     450#if PLATFORM(DARWIN)
     451  thread_resume(platformThread);
     452#elif PLATFORM(WIN_OS)
     453  ResumeThread(platformThread.handle);
     454#else
     455#error Need a way to resume threads on this platform
     456#endif
     457}
     458
    391459typedef unsigned long usword_t; // word size, assumed to be either 32 or 64 bit
    392460
    393 void Collector::markOtherThreadConservatively(Thread *thread)
    394 {
    395   thread_suspend(thread->machThread);
    396 
    397 #if PLATFORM(X86)
    398   i386_thread_state_t regs;
     461#if PLATFORM(DARWIN)
     462
     463#if     PLATFORM(X86)
     464typedef i386_thread_state_t PlatformThreadRegisters;
     465#elif   PLATFORM(X86_64)
     466typedef x86_thread_state64_t PlatformThreadRegisters;
     467#elif   PLATFORM(PPC)
     468typedef ppc_thread_state_t PlatformThreadRegisters;
     469#elif   PLATFORM(PPC64)
     470typedef ppc_thread_state64_t PlatformThreadRegisters;
     471#else
     472#error Unknown Architecture
     473#endif
     474
     475#elif PLATFORM(WIN_OS)&& PLATFORM(X86)
     476typedef CONTEXT PlatformThreadRegisters;
     477#else
     478#error Need a thread register struct for this platform
     479#endif
     480
     481size_t getPlatformThreadRegisters(const PlatformThread& platformThread, PlatformThreadRegisters& regs)
     482{
     483#if PLATFORM(DARWIN)
     484
     485#if     PLATFORM(X86)
    399486  unsigned user_count = sizeof(regs)/sizeof(int);
    400487  thread_state_flavor_t flavor = i386_THREAD_STATE;
    401 #elif PLATFORM(X86_64)
    402   x86_thread_state64_t  regs;
     488#elif   PLATFORM(X86_64)
    403489  unsigned user_count = x86_THREAD_STATE64_COUNT;
    404490  thread_state_flavor_t flavor = x86_THREAD_STATE64;
    405 #elif PLATFORM(PPC)
    406   ppc_thread_state_t  regs;
     491#elif   PLATFORM(PPC)
    407492  unsigned user_count = PPC_THREAD_STATE_COUNT;
    408493  thread_state_flavor_t flavor = PPC_THREAD_STATE;
    409 #elif PLATFORM(PPC64)
    410   ppc_thread_state64_t  regs;
     494#elif   PLATFORM(PPC64)
    411495  unsigned user_count = PPC_THREAD_STATE64_COUNT;
    412496  thread_state_flavor_t flavor = PPC_THREAD_STATE64;
     
    414498#error Unknown Architecture
    415499#endif
    416   // get the thread register state
    417   thread_get_state(thread->machThread, flavor, (thread_state_t)&regs, &user_count);
    418  
    419   // scan the registers
    420   markStackObjectsConservatively((void *)&regs, (void *)((char *)&regs + (user_count * sizeof(usword_t))));
    421    
    422   // scan the stack
    423 #if PLATFORM(X86) && __DARWIN_UNIX03
    424   markStackObjectsConservatively((void *)regs.__esp, pthread_get_stackaddr_np(thread->posixThread));
    425 #elif PLATFORM(X86)
    426   markStackObjectsConservatively((void *)regs.esp, pthread_get_stackaddr_np(thread->posixThread));
    427 #elif PLATFORM(X86_64) && __DARWIN_UNIX03
    428   markStackObjectsConservatively((void *)regs.__rsp, pthread_get_stackaddr_np(thread->posixThread));
     500
     501  thread_get_state(platformThread, flavor, (thread_state_t)&regs, &user_count);
     502  return user_count * sizeof(usword_t);
     503// end PLATFORM(DARWIN)
     504
     505#elif PLATFORM(WIN_OS) && PLATFORM(X86)
     506  regs.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL | CONTEXT_SEGMENTS;
     507  GetThreadContext(platformThread.handle, &regs);
     508  return sizeof(CONTEXT);
     509#else
     510#error Need a way to get thread registers on this platform
     511#endif
     512}
     513
     514static inline void* otherThreadStackPointer(const PlatformThreadRegisters& regs)
     515{
     516#if PLATFORM(DARWIN)
     517
     518#if __DARWIN_UNIX03
     519
     520#if PLATFORM(X86)
     521  return (void*)regs.__esp;
    429522#elif PLATFORM(X86_64)
    430   markStackObjectsConservatively((void *)regs.rsp, pthread_get_stackaddr_np(thread->posixThread));
    431 #elif (PLATFORM(PPC) || PLATFORM(PPC64)) && __DARWIN_UNIX03
    432   markStackObjectsConservatively((void *)regs.__r1, pthread_get_stackaddr_np(thread->posixThread));
     523  return (void*)regs.__rsp;
    433524#elif PLATFORM(PPC) || PLATFORM(PPC64)
    434   markStackObjectsConservatively((void *)regs.r1, pthread_get_stackaddr_np(thread->posixThread));
     525  return (void*)regs.__r1;
    435526#else
    436527#error Unknown Architecture
    437528#endif
    438529
    439   thread_resume(thread->machThread);
     530#else // !__DARWIN_UNIX03
     531
     532#if PLATFORM(X86)
     533  return (void*)regs.esp;
     534#elif PLATFORM(X86_64)
     535  return (void*)regs.rsp;
     536#elif (PLATFORM(PPC) || PLATFORM(PPC64))
     537  return (void*)regs.r1;
     538#else
     539#error Unknown Architecture
     540#endif
     541
     542#endif // __DARWIN_UNIX03
     543
     544// end PLATFORM(DARWIN)
     545#elif PLATFORM(X86) && PLATFORM(WIN_OS)
     546  return (void*)(uintptr_t)regs.Esp;
     547#else
     548#error Need a way to get the stack pointer for another thread on this platform
     549#endif
     550}
     551
     552static inline void* otherThreadStackBase(const PlatformThreadRegisters& regs, Collector::Thread* thread)
     553{
     554#if PLATFORM(DARWIN)
     555  (void)regs;
     556  return pthrad_get_stackaddr_np(thread->posixThread);
     557// end PLATFORM(DARWIN);
     558#elif PLATFORM(X86) && PLATFORM(WIN_OS)
     559  LDT_ENTRY desc;
     560  NT_TIB* tib;
     561  GetThreadSelectorEntry(thread->platformThread.handle, regs.SegFs, &desc);
     562  tib = (NT_TIB*)(uintptr_t)(desc.BaseLow | desc.HighWord.Bytes.BaseMid << 16 | desc.HighWord.Bytes.BaseHi << 24);
     563  ASSERT(tib == tib->Self);
     564  return tib->StackBase;
     565#else
     566#error Need a way to get the stack pointer for another thread on this platform
     567#endif
     568}
     569
     570void Collector::markOtherThreadConservatively(Thread* thread)
     571{
     572  suspendThread(thread->platformThread);
     573
     574  PlatformThreadRegisters regs;
     575  size_t regSize = getPlatformThreadRegisters(thread->platformThread, regs);
     576
     577  // mark the thread's registers
     578  markStackObjectsConservatively((void*)&regs, (void*)((char*)&regs + regSize));
     579 
     580  void* stackPointer = otherThreadStackPointer(regs);
     581  void* stackBase = otherThreadStackBase(regs, thread);
     582  markStackObjectsConservatively(stackPointer, stackBase);
     583
     584  resumeThread(thread->platformThread);
    440585}
    441586
     
    448593#if USE(MULTIPLE_THREADS)
    449594  for (Thread *thread = registeredThreads; thread != NULL; thread = thread->next) {
    450     if (thread->posixThread != pthread_self()) {
     595    if (!pthread_equal(thread->posixThread, pthread_self())) {
    451596      markOtherThreadConservatively(thread);
    452597    }
     
    465610void Collector::protect(JSValue *k)
    466611{
    467     assert(k);
    468     assert(JSLock::lockCount() > 0);
     612    ASSERT(k);
     613    ASSERT(JSLock::lockCount() > 0);
     614    ASSERT(JSLock::currentThreadIsHoldingLock());
    469615
    470616    if (JSImmediate::isImmediate(k))
     
    476622void Collector::unprotect(JSValue *k)
    477623{
    478     assert(k);
    479     assert(JSLock::lockCount() > 0);
     624    ASSERT(k);
     625    ASSERT(JSLock::lockCount() > 0);
     626    ASSERT(JSLock::currentThreadIsHoldingLock());
    480627
    481628    if (JSImmediate::isImmediate(k))
     
    489636    ASSERT(value);
    490637    ASSERT(JSLock::lockCount() > 0);
     638    ASSERT(JSLock::currentThreadIsHoldingLock());
    491639
    492640    if (JSImmediate::isImmediate(value))
     
    512660{
    513661#if USE(MULTIPLE_THREADS)
    514     ASSERT(!pthread_main_np());
     662    ASSERT(!onMainThread());
    515663#endif
    516664
     
    563711bool Collector::collect()
    564712{
    565   assert(JSLock::lockCount() > 0);
     713  ASSERT(JSLock::lockCount() > 0);
     714  ASSERT(JSLock::currentThreadIsHoldingLock());
     715
    566716
    567717#ifndef NDEBUG
     
    570720 
    571721#if USE(MULTIPLE_THREADS)
    572     bool currentThreadIsMainThread = pthread_main_np();
     722    bool currentThreadIsMainThread = onMainThread();
    573723#else
    574724    bool currentThreadIsMainThread = true;
  • trunk/JavaScriptCore/kjs/collector.h

    r20004 r20351  
    5959    static void registerThread();
    6060   
     61    static void registerAsMainThread();
     62
    6163  private:
    6264    Collector();
Note: See TracChangeset for help on using the changeset viewer.