Changeset 15437


Ignore:
Timestamp:
Aug 13, 2012, 8:49:25 AM (7 years ago)
Author:
gb
Message:

Stop trying to get by on our good looks and charm (or at least
stop trying to assume that x8664 Darwin's malloc() will quickly
return a 32-bit pointer in allocate_tcr(). See ticket:1005.)
Instead, try to map a largish (1K) number of TCRs in free 32-bit
memory and manage them explicitly on x8664 Darwin.

Note that this isn't thread-safe in general: we do this by walking
our address space (via vm_region_64()) until we find a free 32-bit
block of memory and using mmap() (with the MAP_FIXED option). When
this happens at any time after application startup, it's possible
for some foreign thread to be mapping/unmapping regions while we're
doing this. (This is why OSes that provide mmap options that request
32-bit addresses do so in the kernel.) It's likely fairly hard in
practice to exceed the 1K initial TCR allocation and it's not clear
that this is any worse than the "wait until we get lucky with malloc()"
strategy has been, but it may be better to just do the TCR allocation
once on startup, avoid the (theoretical) thread-safety issues, and
treat the (possibly raised) value of TCR_CLUSTER_COUNT as a hard limit.

lisp-kernel/platform-darwinx8664: define DARWIN64, to make conditionalization
a little easier
lisp-kernel/memory.c: implement darwin_allocate_tcr() and darwin_free_tcr()
as outlined above
lisp-kernel/thread_manager.c: allocate_tcr() uses darwin_allocate_tcr() on
DARWIN64. Use darwin_free_tcr() instead of free() on DARWIN64. Make
shutdown_thread_tcr() dequeue the TCR and put it back in the free TCR
pool on DARWIN64.

Location:
trunk/source/lisp-kernel
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/source/lisp-kernel/memory.c

    r15373 r15437  
    3232#include <strings.h>
    3333#endif
     34#ifdef DARWIN64
     35#include <pthread.h>
     36#endif
    3437
    3538#ifndef WINDOWS
     
    972975}
    973976
     977#ifdef DARWIN64
     978/*
     979  On 64-bit Darwin, we try to make a TCR's address serve as a Mach port
     980  name, which means that it has to fit in 32 bits (and not conflict with
     981  an existing port name, but that's a separate issue.)  Darwin doesn't
     982  seem to offer means of mapping/allocating memory that's guaranteed to
     983  return a 32-bit address on 64-bit systems, and trial-and-error doesn't
     984  scale well.
     985 
     986  Since it's a PITA to allocate 32-bit TCR pointers, we never free them
     987  once we've done so.  (We maintain a queue of "freed" TCRs but never
     988  unmap the memory.)  When we need to allocate TCR pointers, we try to
     989  allocate substantially more than we need.
     990
     991  The bulk allocation works by scanning the task's mapped memory regions
     992  until a free region of appropriate size is found, then mapping that
     993  region.  There is no way that I know of to prevent a foreign thread
     994  from trying to map this region while we're doing so.
     995*/
     996
     997pthread_mutex_t darwin_tcr_lock = PTHREAD_MUTEX_INITIALIZER;
     998
     999TCR _free_tcr_queue, *darwin_tcr_freelist=&_free_tcr_queue;
     1000
     1001#define TCR_CLUSTER_COUNT 1024   /* Enough that we allocate clusters rarely,
     1002but not so much that we waste lots of 32-bit memory. */
     1003
     1004void
     1005map_tcr_cluster(TCR *head)
     1006{
     1007  TCR *work = NULL, *prev = head;
     1008  int i;
     1009  vm_address_t addr = (vm_address_t)0, nextaddr;
     1010
     1011  vm_size_t request_size = align_to_power_of_2((TCR_CLUSTER_COUNT*sizeof(TCR)),log2_page_size), vm_size;
     1012  vm_region_basic_info_data_64_t vm_info;
     1013  mach_msg_type_number_t vm_info_size = VM_REGION_BASIC_INFO_COUNT_64;
     1014  mach_port_t vm_object_name = (mach_port_t) 0;
     1015  kern_return_t kret;
     1016
     1017  while (1) {
     1018    nextaddr = addr;
     1019    vm_info_size = VM_REGION_BASIC_INFO_COUNT_64;
     1020    kret = vm_region_64(mach_task_self(),
     1021                        &nextaddr,
     1022                        &vm_size,
     1023                        VM_REGION_BASIC_INFO_64,
     1024                        (vm_region_info_t)&vm_info,
     1025                        &vm_info_size,
     1026                        &vm_object_name);
     1027    if (kret != KERN_SUCCESS) {
     1028      break;
     1029    }
     1030    if (addr && ((nextaddr - addr) > request_size)) {
     1031      if ((addr + request_size) > (1L << 32L)) {
     1032        break;
     1033      }
     1034      if (mmap((void *)addr,
     1035               request_size,
     1036               PROT_READ|PROT_WRITE,
     1037               MAP_PRIVATE|MAP_ANON|MAP_FIXED,
     1038               -1,
     1039               0) != (void *)addr) {
     1040        break;
     1041      }
     1042      work = (TCR *)addr;
     1043      break;
     1044    }
     1045    addr = nextaddr + vm_size;   
     1046  }
     1047  if (!work) {
     1048    Fatal("Can't allocate memory for thread-local storage.", "");
     1049  }
     1050 
     1051  for (i=0; i < TCR_CLUSTER_COUNT; i++, work++) {
     1052    prev->next = work;
     1053    work->prev = prev;
     1054    head->prev = work;
     1055    work->next = head;
     1056    prev = work;
     1057  }
     1058}
     1059
     1060void
     1061darwin_free_tcr(TCR *tcr)
     1062{
     1063  TCR  *head = darwin_tcr_freelist, *tail;
     1064
     1065  pthread_mutex_lock(&darwin_tcr_lock);
     1066  tail = head->prev;
     1067  tail->next = tcr;
     1068  head->prev = tcr;
     1069  tcr->prev = tail;
     1070  tcr->next = head;
     1071  pthread_mutex_unlock(&darwin_tcr_lock);
     1072}
     1073
     1074TCR *
     1075darwin_allocate_tcr()
     1076{
     1077  TCR  *head = darwin_tcr_freelist, *tail, *tcr;
     1078  pthread_mutex_lock(&darwin_tcr_lock);
     1079  if (head->next == NULL) { /* First time */
     1080    head->next = head->prev = head;
     1081  }
     1082
     1083  if (head->next == head) {
     1084    map_tcr_cluster(head);
     1085  }
     1086  tcr = head->next;
     1087  tail = tcr->next;
     1088  tail->prev = head;
     1089  head->next = tail;
     1090  pthread_mutex_unlock(&darwin_tcr_lock);
     1091  return tcr;
     1092}
     1093 
     1094
     1095
     1096
     1097#endif
  • trunk/source/lisp-kernel/platform-darwinx8664.h

    r15147 r15437  
    9090
    9191#include "os-darwin.h"
     92
     93#define DARWIN64 1
  • trunk/source/lisp-kernel/thread_manager.c

    r15425 r15437  
    829829#ifdef DARWIN
    830830  extern Boolean use_mach_exception_handling;
     831#ifdef DARWIN64
     832  extern TCR* darwin_allocate_tcr(void);
     833  extern void darwin_free_tcr(TCR *);
     834#endif
    831835  kern_return_t kret;
    832836  mach_port_t
     
    835839#endif
    836840  for (;;) {
    837     tcr = calloc(1, sizeof(TCR));
    838 #ifdef DARWIN
    839 #if WORD_SIZE == 64
    840     if (((unsigned)((natural)tcr)) != ((natural)tcr)) {
    841       tcr->next = chain;
    842       chain = tcr;
    843       continue;
    844     }
    845 #endif
     841#ifdef DARWIN64
     842    tcr = darwin_allocate_tcr();
    846843    if (use_mach_exception_handling) {
    847844      thread_exception_port = (mach_port_t)((natural)tcr);
     
    858855      continue;
    859856    }
     857#else
     858    tcr = calloc(1, sizeof(TCR));
    860859#endif
    861860    for (;chain;chain = next) {
    862861      next = chain->next;
     862#ifdef DARWIN64
     863      darwin_free_tcr(chain);
     864#else
    863865      free(chain);
     866#endif
    864867    }
    865868    return tcr;
     
    13581361shutdown_thread_tcr(void *arg)
    13591362{
     1363#ifdef DARWIN64
     1364  extern void darwin_free_tcr(TCR *);
     1365#endif
    13601366  TCR *tcr = TCR_FROM_TSD(arg),*current=get_tcr(0);
    13611367
     
    14331439    tcr->interrupt_pending = 0;
    14341440    TCR_AUX(tcr)->termination_semaphore = NULL;
    1435 #if defined(HAVE_TLS) || defined(WIN_32)
     1441#if defined(HAVE_TLS) || defined(WIN_32) || defined(DARWIN64)
    14361442    dequeue_tcr(tcr);
    14371443#endif
     
    14481454    tcr->aux = NULL;
    14491455#endif
     1456#endif
     1457#ifdef DARWIN64
     1458    darwin_free_tcr(tcr);
    14501459#endif
    14511460    UNLOCK(lisp_global(TCR_AREA_LOCK),current);
     
    23062315free_freed_tcrs ()
    23072316{
     2317#ifdef DARWIN64
     2318  extern void darwin_free_tcr(TCR *);
     2319#endif
    23082320  TCR *current, *next;
    23092321
     
    23152327     * tcr aux vector elsewhere. */
    23162328#else
     2329#ifdef DARWIN64
     2330    darwin_free_tcr(current);
     2331#else
    23172332    free(current);
     2333#endif
    23182334#endif
    23192335#endif
Note: See TracChangeset for help on using the changeset viewer.