/****************************************************************//** Frees large pages memory. */ UNIV_INTERN void os_mem_free_large( /*==============*/ void *ptr, /*!< in: pointer returned by os_mem_alloc_large() */ ulint size) /*!< in: size returned by os_mem_alloc_large() */ { os_fast_mutex_lock(&ut_list_mutex); ut_a(ut_total_allocated_memory >= size); os_fast_mutex_unlock(&ut_list_mutex); #if defined HAVE_LARGE_PAGES && defined UNIV_LINUX if (os_use_large_pages && os_large_page_size && !shmdt(ptr)) { os_fast_mutex_lock(&ut_list_mutex); ut_a(ut_total_allocated_memory >= size); ut_total_allocated_memory -= size; os_fast_mutex_unlock(&ut_list_mutex); UNIV_MEM_FREE(ptr, size); return; } #endif /* HAVE_LARGE_PAGES && UNIV_LINUX */ #ifdef __WIN__ /* When RELEASE memory, the size parameter must be 0. Do not use MEM_RELEASE with MEM_DECOMMIT. */ if (!VirtualFree(ptr, 0, MEM_RELEASE)) { fprintf(stderr, "InnoDB: VirtualFree(%p, %lu) failed;" " Windows error %lu\n", ptr, (ulong) size, (ulong) GetLastError()); } else { os_fast_mutex_lock(&ut_list_mutex); ut_a(ut_total_allocated_memory >= size); ut_total_allocated_memory -= size; os_fast_mutex_unlock(&ut_list_mutex); UNIV_MEM_FREE(ptr, size); } #elif !defined OS_MAP_ANON ut_free(ptr); #else if (munmap(ptr, size)) { fprintf(stderr, "InnoDB: munmap(%p, %lu) failed;" " errno %lu\n", ptr, (ulong) size, (ulong) errno); } else { os_fast_mutex_lock(&ut_list_mutex); ut_a(ut_total_allocated_memory >= size); ut_total_allocated_memory -= size; os_fast_mutex_unlock(&ut_list_mutex); UNIV_MEM_FREE(ptr, size); } #endif }
/**********************************************************************//** Frees a memory block allocated with ut_malloc. Freeing a NULL pointer is a nop. */ UNIV_INTERN void ut_free( /*====*/ void* ptr) /*!< in, own: memory block, can be NULL */ { #ifndef UNIV_HOTBACKUP ut_mem_block_t* block; if (ptr == NULL) { return; } else if (UNIV_LIKELY(srv_use_sys_malloc)) { free(ptr); return; } block = (ut_mem_block_t*)((byte*)ptr - sizeof(ut_mem_block_t)); os_fast_mutex_lock(&ut_list_mutex); ut_a(block->magic_n == UT_MEM_MAGIC_N); ut_a(ut_total_allocated_memory >= block->size); ut_total_allocated_memory -= block->size; UT_LIST_REMOVE(mem_block_list, ut_mem_block_list, block); free(block); os_fast_mutex_unlock(&ut_list_mutex); #else /* !UNIV_HOTBACKUP */ free(ptr); #endif /* !UNIV_HOTBACKUP */ }
/**********************************************************//** Acquires ownership of a mutex semaphore. */ UNIV_INTERN void os_mutex_enter( /*===========*/ os_mutex_t mutex) /*!< in: mutex to acquire */ { #ifdef __WIN__ DWORD err; ut_a(mutex); /* Specify infinite time limit for waiting */ err = WaitForSingleObject(mutex->handle, INFINITE); ut_a(err == WAIT_OBJECT_0); (mutex->count)++; ut_a(mutex->count == 1); #else os_fast_mutex_lock(mutex->handle); (mutex->count)++; ut_a(mutex->count == 1); #endif }
/**********************************************************//** Resets an event semaphore to the nonsignaled state. Waiting threads will stop to wait for the event. The return value should be passed to os_even_wait_low() if it is desired that this thread should not wait in case of an intervening call to os_event_set() between this os_event_reset() and the os_event_wait_low() call. See comments for os_event_wait_low(). @return current signal_count. */ UNIV_INTERN ib_int64_t os_event_reset( /*===========*/ os_event_t event) /*!< in: event to reset */ { ib_int64_t ret = 0; #ifdef __WIN__ ut_a(event); ut_a(ResetEvent(event->handle)); #else ut_a(event); os_fast_mutex_lock(&(event->os_mutex)); if (!event->is_set) { /* Do nothing */ } else { event->is_set = FALSE; } ret = event->signal_count; os_fast_mutex_unlock(&(event->os_mutex)); #endif return(ret); }
/**********************************************************//** Sets an event semaphore to the signaled state: lets waiting threads proceed. */ UNIV_INTERN void os_event_set( /*=========*/ os_event_t event) /*!< in: event to set */ { #ifdef __WIN__ ut_a(event); ut_a(SetEvent(event->handle)); #else ut_a(event); os_fast_mutex_lock(&(event->os_mutex)); if (event->is_set) { /* Do nothing */ } else { event->is_set = TRUE; event->signal_count += 1; ut_a(0 == pthread_cond_broadcast(&(event->cond_var))); } os_fast_mutex_unlock(&(event->os_mutex)); #endif }
/**********************************************************//** Waits for an event object until it is in the signaled state. If srv_shutdown_state == SRV_SHUTDOWN_EXIT_THREADS this also exits the waiting thread when the event becomes signaled (or immediately if the event is already in the signaled state). Typically, if the event has been signalled after the os_event_reset() we'll return immediately because event->is_set == TRUE. There are, however, situations (e.g.: sync_array code) where we may lose this information. For example: thread A calls os_event_reset() thread B calls os_event_set() [event->is_set == TRUE] thread C calls os_event_reset() [event->is_set == FALSE] thread A calls os_event_wait() [infinite wait!] thread C calls os_event_wait() [infinite wait!] Where such a scenario is possible, to avoid infinite wait, the value returned by os_event_reset() should be passed in as reset_sig_count. */ UNIV_INTERN void os_event_wait_low( /*==============*/ os_event_t event, /*!< in: event to wait */ ib_int64_t reset_sig_count)/*!< in: zero or the value returned by previous call of os_event_reset(). */ { #ifdef __WIN__ DWORD err; ut_a(event); UT_NOT_USED(reset_sig_count); /* Specify an infinite time limit for waiting */ err = WaitForSingleObject(event->handle, INFINITE); ut_a(err == WAIT_OBJECT_0); if (srv_shutdown_state == SRV_SHUTDOWN_EXIT_THREADS) { os_thread_exit(NULL); } #else ib_int64_t old_signal_count; os_fast_mutex_lock(&(event->os_mutex)); if (reset_sig_count) { old_signal_count = reset_sig_count; } else { old_signal_count = event->signal_count; } for (;;) { if (event->is_set == TRUE || event->signal_count != old_signal_count) { os_fast_mutex_unlock(&(event->os_mutex)); if (srv_shutdown_state == SRV_SHUTDOWN_EXIT_THREADS) { os_thread_exit(NULL); } /* Ok, we may return */ return; } pthread_cond_wait(&(event->cond_var), &(event->os_mutex)); /* Solaris manual said that spurious wakeups may occur: we have to check if the event really has been signaled after we came here to wait */ } #endif }
ib_err_t ib_cfg_shutdown(void) /*=================*/ { os_fast_mutex_lock(&cfg_vars_mutex); /* TODO: Check for NULL values of allocated config variables. */ memset(cfg_vars, 0x0, sizeof(cfg_vars)); os_fast_mutex_unlock(&cfg_vars_mutex); os_fast_mutex_free(&cfg_vars_mutex); return(DB_SUCCESS); }
ib_err_t ib_cfg_get( /*=======*/ const char* name, /*!< in: variable name */ void* value) /*!< out: pointer to the place to store the retrieved value */ { ib_cfg_var_t* cfg_var; ib_err_t ret; os_fast_mutex_lock(&cfg_vars_mutex); cfg_var = ib_cfg_lookup_var(name); if (cfg_var != NULL) { ret = cfg_var->get(cfg_var, value); } else { ret = DB_NOT_FOUND; } os_fast_mutex_unlock(&cfg_vars_mutex); return(ret); }
ib_err_t ib_cfg_var_get_type( /*================*/ const char* name, /*!< in: variable name */ ib_cfg_type_t* type) /*!< out: variable type */ { ib_cfg_var_t* cfg_var; ib_err_t ret; os_fast_mutex_lock(&cfg_vars_mutex); cfg_var = ib_cfg_lookup_var(name); if (cfg_var != NULL) { *type = cfg_var->type; ret = DB_SUCCESS; } else { ret = DB_NOT_FOUND; } os_fast_mutex_unlock(&cfg_vars_mutex); return(ret); }
/**********************************************************************//** Allocates memory. @return own: allocated memory */ UNIV_INTERN void* ut_malloc_low( /*==========*/ ulint n, /*!< in: number of bytes to allocate */ ibool assert_on_error)/*!< in: if TRUE, we crash mysqld if the memory cannot be allocated */ { #ifndef UNIV_HOTBACKUP ulint retry_count; void* ret; if (UNIV_LIKELY(srv_use_sys_malloc)) { ret = malloc(n); ut_a(ret || !assert_on_error); return(ret); } ut_ad((sizeof(ut_mem_block_t) % 8) == 0); /* check alignment ok */ ut_a(ut_mem_block_list_inited); retry_count = 0; retry: os_fast_mutex_lock(&ut_list_mutex); ret = malloc(n + sizeof(ut_mem_block_t)); if (ret == NULL && retry_count < 60) { if (retry_count == 0) { ut_print_timestamp(stderr); fprintf(stderr, " InnoDB: Error: cannot allocate" " %lu bytes of\n" "InnoDB: memory with malloc!" " Total allocated memory\n" "InnoDB: by InnoDB %lu bytes." " Operating system errno: %lu\n" "InnoDB: Check if you should" " increase the swap file or\n" "InnoDB: ulimits of your operating system.\n" "InnoDB: On FreeBSD check you" " have compiled the OS with\n" "InnoDB: a big enough maximum process size.\n" "InnoDB: Note that in most 32-bit" " computers the process\n" "InnoDB: memory space is limited" " to 2 GB or 4 GB.\n" "InnoDB: We keep retrying" " the allocation for 60 seconds...\n", (ulong) n, (ulong) ut_total_allocated_memory, #ifdef __WIN__ (ulong) GetLastError() #else (ulong) errno #endif ); } os_fast_mutex_unlock(&ut_list_mutex); /* Sleep for a second and retry the allocation; maybe this is just a temporary shortage of memory */ os_thread_sleep(1000000); retry_count++; goto retry; } if (ret == NULL) { /* Flush stderr to make more probable that the error message gets in the error file before we generate a seg fault */ fflush(stderr); os_fast_mutex_unlock(&ut_list_mutex); /* Make an intentional seg fault so that we get a stack trace */ if (assert_on_error) { ut_print_timestamp(stderr); fprintf(stderr, " InnoDB: We now intentionally" " generate a seg fault so that\n" "InnoDB: on Linux we get a stack trace.\n"); if (*ut_mem_null_ptr) ut_mem_null_ptr = 0; } else { return(NULL); } } UNIV_MEM_ALLOC(ret, n + sizeof(ut_mem_block_t)); ((ut_mem_block_t*)ret)->size = n + sizeof(ut_mem_block_t); ((ut_mem_block_t*)ret)->magic_n = UT_MEM_MAGIC_N; ut_total_allocated_memory += n + sizeof(ut_mem_block_t); UT_LIST_ADD_FIRST(mem_block_list, ut_mem_block_list, ((ut_mem_block_t*)ret)); os_fast_mutex_unlock(&ut_list_mutex); return((void*)((byte*)ret + sizeof(ut_mem_block_t))); #else /* !UNIV_HOTBACKUP */ void* ret = malloc(n); ut_a(ret || !assert_on_error); return(ret); #endif /* !UNIV_HOTBACKUP */ }
/*********************************************************************//** Set a configuration variable. "ap" must contain one argument whose type depends on the type of the variable with the given "name". Returns DB_SUCCESS if the variable with name "name" was found and if its value was set. ib_cfg_set_ap() @{ @return DB_SUCCESS if set */ static ib_err_t ib_cfg_set_ap( /*==========*/ const char* name, /*!< in: variable name */ va_list ap) /*!< in: variable value */ { ib_cfg_var_t* cfg_var; ib_err_t ret = DB_NOT_FOUND; os_fast_mutex_lock(&cfg_vars_mutex); cfg_var = ib_cfg_lookup_var(name); if (cfg_var != NULL) { /* check whether setting the variable is appropriate, according to its flag */ if (cfg_var->flag & IB_CFG_FLAG_READONLY || (cfg_var->flag & IB_CFG_FLAG_READONLY_AFTER_STARTUP && srv_was_started)) { ret = DB_READONLY; } else { /* Get the parameter according to its type and call ::set() */ switch (cfg_var->type) { case IB_CFG_IBOOL: { ib_bool_t value; value = va_arg(ap, ib_bool_t); ret = cfg_var->set(cfg_var, &value); break; } case IB_CFG_ULINT: { ulint value; value = va_arg(ap, ulint); ret = cfg_var->set(cfg_var, &value); break; } case IB_CFG_ULONG: { ulong value; value = va_arg(ap, ulong); ret = cfg_var->set(cfg_var, &value); break; } case IB_CFG_TEXT: { const char* value; value = va_arg(ap, const char*); ret = cfg_var->set(cfg_var, &value); break; } case IB_CFG_CB: { ib_cb_t value; value = va_arg(ap, ib_cb_t); ret = cfg_var->set(cfg_var, &value); break; } /* do not add default: in order to produce a compilation warning if new type is added which is not handled here */ } } } os_fast_mutex_unlock(&cfg_vars_mutex); return(ret); }
/****************************************************************//** Allocates large pages memory. @return allocated memory */ UNIV_INTERN void* os_mem_alloc_large( /*===============*/ ulint* n) /*!< in/out: number of bytes */ { void* ptr; ulint size; #if defined HAVE_LARGE_PAGES && defined UNIV_LINUX int shmid; struct shmid_ds buf; if (!os_use_large_pages || !os_large_page_size) { goto skip; } /* Align block size to os_large_page_size */ ut_ad(ut_is_2pow(os_large_page_size)); size = ut_2pow_round(*n + (os_large_page_size - 1), os_large_page_size); shmid = shmget(IPC_PRIVATE, (size_t)size, SHM_HUGETLB | SHM_R | SHM_W); if (shmid < 0) { fprintf(stderr, "InnoDB: HugeTLB: Warning: Failed to allocate" " %lu bytes. errno %d\n", size, errno); ptr = NULL; } else { ptr = shmat(shmid, NULL, 0); if (ptr == (void *)-1) { fprintf(stderr, "InnoDB: HugeTLB: Warning: Failed to" " attach shared memory segment, errno %d\n", errno); ptr = NULL; } /* Remove the shared memory segment so that it will be automatically freed after memory is detached or process exits */ shmctl(shmid, IPC_RMID, &buf); } if (ptr) { *n = size; os_fast_mutex_lock(&ut_list_mutex); ut_total_allocated_memory += size; os_fast_mutex_unlock(&ut_list_mutex); UNIV_MEM_ALLOC(ptr, size); return(ptr); } fprintf(stderr, "InnoDB HugeTLB: Warning: Using conventional" " memory pool\n"); skip: #endif /* HAVE_LARGE_PAGES && UNIV_LINUX */ #ifdef __WIN__ SYSTEM_INFO system_info; GetSystemInfo(&system_info); /* Align block size to system page size */ ut_ad(ut_is_2pow(system_info.dwPageSize)); /* system_info.dwPageSize is only 32-bit. Casting to ulint is required on 64-bit Windows. */ size = *n = ut_2pow_round(*n + (system_info.dwPageSize - 1), (ulint) system_info.dwPageSize); ptr = VirtualAlloc(NULL, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); if (!ptr) { fprintf(stderr, "InnoDB: VirtualAlloc(%lu bytes) failed;" " Windows error %lu\n", (ulong) size, (ulong) GetLastError()); } else { os_fast_mutex_lock(&ut_list_mutex); ut_total_allocated_memory += size; os_fast_mutex_unlock(&ut_list_mutex); UNIV_MEM_ALLOC(ptr, size); } #elif !defined OS_MAP_ANON size = *n; ptr = ut_malloc_low(size, TRUE, FALSE); #else # ifdef HAVE_GETPAGESIZE size = getpagesize(); # else size = UNIV_PAGE_SIZE; # endif /* Align block size to system page size */ ut_ad(ut_is_2pow(size)); size = *n = ut_2pow_round(*n + (size - 1), size); ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | OS_MAP_ANON, -1, 0); if (UNIV_UNLIKELY(ptr == (void*) -1)) { fprintf(stderr, "InnoDB: mmap(%lu bytes) failed;" " errno %lu\n", (ulong) size, (ulong) errno); ptr = NULL; } else { os_fast_mutex_lock(&ut_list_mutex); ut_total_allocated_memory += size; os_fast_mutex_unlock(&ut_list_mutex); UNIV_MEM_ALLOC(ptr, size); } #endif return(ptr); }