📄 thread_local_alloc.c
字号:
/* * Copyright (c) 2000-2005 by Hewlett-Packard Company. All rights reserved. * * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. * * Permission is hereby granted to use or copy this program * for any purpose, provided the above notices are retained on all copies. * Permission to modify the code and to distribute modified code is granted, * provided the above notices are retained, and a notice that the code was * modified is included with the above copyright notice. */#include "private/gc_priv.h"# if defined(THREAD_LOCAL_ALLOC)#include "private/thread_local_alloc.h"#include "gc_inline.h"# include <stdlib.h>#if defined(USE_COMPILER_TLS) __thread#elif defined(USE_WIN32_COMPILER_TLS) __declspec(thread)#endifGC_key_t GC_thread_key;static GC_bool keys_initialized;/* Return a single nonempty freelist fl to the global one pointed to *//* by gfl. */static void return_single_freelist(void *fl, void **gfl){ void *q, **qptr; if (*gfl == 0) { *gfl = fl; } else { GC_ASSERT(GC_size(fl) == GC_size(*gfl)); /* Concatenate: */ for (qptr = &(obj_link(fl)), q = *qptr; (word)q >= HBLKSIZE; qptr = &(obj_link(q)), q = *qptr); GC_ASSERT(0 == q); *qptr = *gfl; *gfl = fl; }}/* Recover the contents of the freelist array fl into the global one gfl.*//* We hold the allocator lock. */static void return_freelists(void **fl, void **gfl){ int i; for (i = 1; i < TINY_FREELISTS; ++i) { if ((word)(fl[i]) >= HBLKSIZE) { return_single_freelist(fl[i], gfl+i); } /* Clear fl[i], since the thread structure may hang around. */ /* Do it in a way that is likely to trap if we access it. */ fl[i] = (ptr_t)HBLKSIZE; } /* The 0 granule freelist really contains 1 granule objects. */# ifdef GC_GCJ_SUPPORT if (fl[0] == ERROR_FL) return;# endif if ((word)(fl[0]) >= HBLKSIZE) { return_single_freelist(fl[0], gfl+1); }}/* Each thread structure must be initialized. *//* This call must be made from the new thread. *//* Caller holds allocation lock. */void GC_init_thread_local(GC_tlfs p){ int i; if (!keys_initialized) { if (0 != GC_key_create(&GC_thread_key, 0)) { ABORT("Failed to create key for local allocator"); } keys_initialized = TRUE; } if (0 != GC_setspecific(GC_thread_key, p)) { ABORT("Failed to set thread specific allocation pointers"); } for (i = 1; i < TINY_FREELISTS; ++i) { p -> ptrfree_freelists[i] = (void *)1; p -> normal_freelists[i] = (void *)1;# ifdef GC_GCJ_SUPPORT p -> gcj_freelists[i] = (void *)1;# endif } /* Set up the size 0 free lists. */ /* We now handle most of them like regular free lists, to ensure */ /* That explicit deallocation works. However, allocation of a */ /* size 0 "gcj" object is always an error. */ p -> ptrfree_freelists[0] = (void *)1; p -> normal_freelists[0] = (void *)1;# ifdef GC_GCJ_SUPPORT p -> gcj_freelists[0] = ERROR_FL;# endif}#ifdef GC_GCJ_SUPPORT extern void ** GC_gcjobjfreelist;#endif/* We hold the allocator lock. */void GC_destroy_thread_local(GC_tlfs p){ /* We currently only do this from the thread itself or from */ /* the fork handler for a child process. */# ifndef HANDLE_FORK GC_ASSERT(GC_getspecific(GC_thread_key) == (void *)p);# endif return_freelists(p -> ptrfree_freelists, GC_aobjfreelist); return_freelists(p -> normal_freelists, GC_objfreelist);# ifdef GC_GCJ_SUPPORT return_freelists(p -> gcj_freelists, GC_gcjobjfreelist);# endif}#if defined(GC_ASSERTIONS) && defined(GC_PTHREADS) && !defined(CYGWIN32) \ && !defined(GC_WIN32_PTHREADS)# include <pthread.h> extern char * GC_lookup_thread(pthread_t id);#endif#if defined(GC_ASSERTIONS) && defined(GC_WIN32_THREADS) extern char * GC_lookup_thread(int id);#endifvoid * GC_malloc(size_t bytes){ size_t granules = ROUNDED_UP_GRANULES(bytes); void *tsd; void *result; void **tiny_fl;# if defined(REDIRECT_MALLOC) && !defined(USE_PTHREAD_SPECIFIC) GC_key_t k = GC_thread_key; if (EXPECT(0 == k, 0)) { /* We haven't yet run GC_init_parallel. That means */ /* we also aren't locking, so this is fairly cheap. */ return GC_core_malloc(bytes); } tsd = GC_getspecific(k);# else GC_ASSERT(GC_is_initialized); tsd = GC_getspecific(GC_thread_key);# endif# if defined(REDIRECT_MALLOC) && defined(USE_PTHREAD_SPECIFIC) if (EXPECT(NULL == tsd, 0)) { return GC_core_malloc(bytes); }# endif# ifdef GC_ASSERTIONS /* We can't check tsd correctly, since we don't have access to */ /* the right declarations. But we can check that it's close. */ LOCK(); {# if defined(GC_WIN32_THREADS) char * me = (char *)GC_lookup_thread_inner(GetCurrentThreadId());# else char * me = GC_lookup_thread(pthread_self());# endif GC_ASSERT((char *)tsd > me && (char *)tsd < me + 1000); } UNLOCK();# endif tiny_fl = ((GC_tlfs)tsd) -> normal_freelists; GC_FAST_MALLOC_GRANS(result, granules, tiny_fl, DIRECT_GRANULES, NORMAL, GC_core_malloc(bytes), obj_link(result)=0); return result;}void * GC_malloc_atomic(size_t bytes){ size_t granules = ROUNDED_UP_GRANULES(bytes); void *result; void **tiny_fl; GC_ASSERT(GC_is_initialized); tiny_fl = ((GC_tlfs)GC_getspecific(GC_thread_key)) -> ptrfree_freelists; GC_FAST_MALLOC_GRANS(result, bytes, tiny_fl, DIRECT_GRANULES, PTRFREE, GC_core_malloc_atomic(bytes), 0/* no init */); return result;}#ifdef GC_GCJ_SUPPORT#include "include/gc_gcj.h"#ifdef GC_ASSERTIONS extern GC_bool GC_gcj_malloc_initialized;#endifextern int GC_gcj_kind;/* Gcj-style allocation without locks is extremely tricky. The *//* fundamental issue is that we may end up marking a free list, which *//* has freelist links instead of "vtable" pointers. That is usually *//* OK, since the next object on the free list will be cleared, and *//* will thus be interpreted as containg a zero descriptor. That's fine *//* if the object has not yet been initialized. But there are *//* interesting potential races. *//* In the case of incremental collection, this seems hopeless, since *//* the marker may run asynchronously, and may pick up the pointer to *//* the next freelist entry (which it thinks is a vtable pointer), get *//* suspended for a while, and then see an allocated object instead *//* of the vtable. This made be avoidable with either a handshake with *//* the collector or, probably more easily, by moving the free list *//* links to the second word of each object. The latter isn't a *//* universal win, since on architecture like Itanium, nonzero offsets *//* are not necessarily free. And there may be cache fill order issues. *//* For now, we punt with incremental GC. This probably means that *//* incremental GC should be enabled before we fork a second thread. */void * GC_gcj_malloc(size_t bytes, void * ptr_to_struct_containing_descr){ if (GC_EXPECT(GC_incremental, 0)) { return GC_core_gcj_malloc(bytes, ptr_to_struct_containing_descr); } else { size_t granules = ROUNDED_UP_GRANULES(bytes); void *result; void **tiny_fl = ((GC_tlfs)GC_getspecific(GC_thread_key)) -> gcj_freelists; GC_ASSERT(GC_gcj_malloc_initialized); GC_FAST_MALLOC_GRANS(result, bytes, tiny_fl, DIRECT_GRANULES, GC_gcj_kind, GC_core_gcj_malloc(bytes, ptr_to_struct_containing_descr), {AO_compiler_barrier(); *(void **)result = ptr_to_struct_containing_descr;}); /* This forces the initialization of the "method ptr". */ /* This is necessary to ensure some very subtle properties */ /* required if a GC is run in the middle of such an allocation. */ /* Here we implicitly also assume atomicity for the free list. */ /* and method pointer assignments. */ /* We must update the freelist before we store the pointer. */ /* Otherwise a GC at this point would see a corrupted */ /* free list. */ /* A real memory barrier is not needed, since the */ /* action of stopping this thread will cause prior writes */ /* to complete. */ /* We assert that any concurrent marker will stop us. */ /* Thus it is impossible for a mark procedure to see the */ /* allocation of the next object, but to see this object */ /* still containing a free list pointer. Otherwise the */ /* marker, by misinterpreting the freelist link as a vtable */ /* pointer, might find a random "mark descriptor" in the next */ /* object. */ return result; }}#endif /* GC_GCJ_SUPPORT *//* The thread support layer must arrange to mark thread-local *//* free lists explicitly, since the link field is often *//* invisible to the marker. It knows hoe to find all threads; *//* we take care of an individual thread freelist structure. */void GC_mark_thread_local_fls_for(GC_tlfs p){ ptr_t q; int j; for (j = 1; j < TINY_FREELISTS; ++j) { q = p -> ptrfree_freelists[j]; if ((word)q > HBLKSIZE) GC_set_fl_marks(q); q = p -> normal_freelists[j]; if ((word)q > HBLKSIZE) GC_set_fl_marks(q);# ifdef GC_GCJ_SUPPORT q = p -> gcj_freelists[j]; if ((word)q > HBLKSIZE) GC_set_fl_marks(q);# endif /* GC_GCJ_SUPPORT */ }}#if defined(GC_ASSERTIONS) /* Check that all thread-local free-lists in p are completely marked. */ void GC_check_tls_for(GC_tlfs p) { ptr_t q; int j; for (j = 1; j < TINY_FREELISTS; ++j) { q = p -> ptrfree_freelists[j]; if ((word)q > HBLKSIZE) GC_check_fl_marks(q); q = p -> normal_freelists[j]; if ((word)q > HBLKSIZE) GC_check_fl_marks(q);# ifdef GC_GCJ_SUPPORT q = p -> gcj_freelists[j]; if ((word)q > HBLKSIZE) GC_check_fl_marks(q);# endif /* GC_GCJ_SUPPORT */ } }#endif /* GC_ASSERTIONS */# else /* !THREAD_LOCAL_ALLOC */# define GC_destroy_thread_local(t)# endif /* !THREAD_LOCAL_ALLOC */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -