Question

I am intrigued by how V8's scopes work.

How can a scope object on the stack find other scope objects and contexts further up the stack?

Digging into how HandleScopes worked I found that they rely on thread locals. This has left me wondering how these work in C++, I've found the implementation but still don't feel I understand what's going on.

api.cc -- HandleScope looks for the current Isolate

HandleScope::HandleScope() {
  i::Isolate* isolate = i::Isolate::Current();
  API_ENTRY_CHECK(isolate, "HandleScope::HandleScope");
  v8::ImplementationUtilities::HandleScopeData* current =
      isolate->handle_scope_data();
  isolate_ = isolate;
  prev_next_ = current->next;
  prev_limit_ = current->limit;
  is_closed_ = false;
  current->level++;
}

isolate.cc -- static method looks for the current isolate as thread local

  // Returns the isolate inside which the current thread is running.
  INLINE(static Isolate* Current()) {
    const Thread::LocalStorageKey key = isolate_key();
    Isolate* isolate = reinterpret_cast<Isolate*>(
        Thread::GetExistingThreadLocal(key));
    if (!isolate) {
      EnsureDefaultIsolate();
      isolate = reinterpret_cast<Isolate*>(
          Thread::GetExistingThreadLocal(key));
    }
    ASSERT(isolate != NULL);
    return isolate;
  }

platform.h -- calls a low level method to retrieve thread local

  static inline void* GetExistingThreadLocal(LocalStorageKey key) {
    void* result = reinterpret_cast<void*>(
        InternalGetExistingThreadLocal(static_cast<intptr_t>(key)));
    ASSERT(result == GetThreadLocal(key));
    return result;
  }

platform-tls-win32.h -- the magic happens

inline intptr_t InternalGetExistingThreadLocal(intptr_t index) {
  const intptr_t kTibInlineTlsOffset = 0xE10;
  const intptr_t kTibExtraTlsOffset = 0xF94;
  const intptr_t kMaxInlineSlots = 64;
  const intptr_t kMaxSlots = kMaxInlineSlots + 1024;
  ASSERT(0 <= index && index < kMaxSlots);
  if (index < kMaxInlineSlots) {
    return static_cast<intptr_t>(__readfsdword(kTibInlineTlsOffset +
                                               kPointerSize * index));
  }
  intptr_t extra = static_cast<intptr_t>(__readfsdword(kTibExtraTlsOffset));
  ASSERT(extra != 0);
  return *reinterpret_cast<intptr_t*>(extra +
                                      kPointerSize * (index - kMaxInlineSlots));
}
  • How exactly is this last method working?
  • How does it know where to look?
  • What is the structure of the stack?
Was it helpful?

Solution

You can view InternalGetExistingThreadLocal as an inline version of TlsGetValue WinAPI call.

On Windows in user mode fs segment register allows code to access Thread Information Block (TIB) which contains thread specific information, for example Thread Local Storage structures.

Layout of TIB and the way TLS is stored inside TIB is exposed in DDK (see http://en.wikipedia.org/wiki/Win32_Thread_Information_Block for quick overview of the TIB layout).

Given this knowledge and ability to read data from TIB via __readfsdword(offs) (which is equivalent of reading dword ptr fs:[offs]) one can directly and efficiently access TLS without calling TlsGetValue.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top