/** * Create new aging container, where keys/values expire and need to be freed. * Values are either integers (cast to pointers) or refer to real objects. * * @param delay the aging delay, in seconds, for entries * @param hash the hashing function for the keys in the hash table * @param eq the equality function for the keys in the hash table * @param kvfree the key/value pair freeing callback, NULL if none. * * @return opaque handle to the container. */ aging_table_t * aging_make(int delay, hash_fn_t hash, eq_fn_t eq, free_keyval_fn_t kvfree) { aging_table_t *ag; WALLOC0(ag); ag->magic = AGING_MAGIC; ag->table = hikset_create_any( offsetof(struct aging_value, key), NULL == hash ? pointer_hash : hash, eq); ag->kvfree = kvfree; delay = MAX(delay, 1); delay = MIN(delay, INT_MAX / 1000); ag->delay = delay; elist_init(&ag->list, offsetof(struct aging_value, lk)); /* * If the callout queue does not run in the thread that is creating * this table, then concurrent accesses are bound to happen. * Therefore, make the table thread-safe. */ if (cq_main_thread_id() != thread_small_id()) aging_thread_safe(ag); ag->gc_ev = cq_periodic_main_add(AGING_CALLOUT, aging_gc, ag); aging_check(ag); return ag; }
/** * Invoked when a fatal signal is received during dladdr(). */ static G_GNUC_COLD void dl_util_got_signal(int signo) { int stid = thread_small_id(); (void) signo; Siglongjmp(dl_util_env[stid], signo); }
NO_INLINE void G_GNUC_COLD assertion_failure_log(const assertion_data * const data, const char * const fmt, ...) { va_list args; const char *msg; assertion_message(data, TRUE); /* * Record the root cause of the assertion failure to be able to log it * in the crash log in case they don't have gdb available. */ crash_assert_failure(data); /* * Record additional message in the crash log as well. */ va_start(args, fmt); msg = crash_assert_logv(fmt, args); va_end(args); /* * Log additional message. */ if (msg != NULL) { char time_buf[18]; char prefix[UINT_DEC_BUFLEN + CONST_STRLEN(" (FATAL-): ")]; unsigned stid = thread_small_id(); DECLARE_STR(4); crash_time(time_buf, sizeof time_buf); print_str(time_buf); if (0 == stid) { print_str(" (FATAL): "); } else { str_bprintf(prefix, sizeof prefix, " (FATAL-%u): ", stid); print_str(prefix); } print_str(msg); print_str("\n"); flush_err_str(); if (log_stdout_is_distinct()) flush_str(STDOUT_FILENO); } assertion_abort(); }
NO_INLINE void G_GNUC_COLD assertion_warning_log(const assertion_data * const data, const char * const fmt, ...) { static str_t *str; va_list args; assertion_message(data, FALSE); if G_UNLIKELY(NULL == str) str = str_new_not_leaking(512); /* * Log additional message. */ va_start(args, fmt); str_vprintf(str, fmt, args); va_end(args); { char time_buf[18]; char prefix[UINT_DEC_BUFLEN + CONST_STRLEN(" (WARNING-): ")]; unsigned stid = thread_small_id(); DECLARE_STR(4); crash_time(time_buf, sizeof time_buf); print_str(time_buf); if (0 == stid) { print_str(" (WARNING): "); } else { str_bprintf(prefix, sizeof prefix, " (WARNING-%u): ", stid); print_str(prefix); } print_str(str_2c(str)); print_str("\n"); flush_err_str(); if (log_stdout_is_distinct()) flush_str(STDOUT_FILENO); } assertion_stacktrace(); }
/** * @note For maximum safety this is kept signal-safe, so that we can * even use assertions in signal handlers. See also: * http://www.opengroup.org/onlinepubs/009695399/functions/xsh_chap02_04.html */ static G_GNUC_COLD void assertion_message(const assertion_data * const data, int fatal) { char line_buf[22]; char time_buf[18]; char prefix[UINT_DEC_BUFLEN + CONST_STRLEN(" (WARNING-): ")]; unsigned stid; DECLARE_STR(16); crash_time(time_buf, sizeof time_buf); stid = thread_small_id(); print_str(time_buf); if (0 == stid) { print_str(fatal ? " (FATAL): " : " (WARNING): "); } else { str_bprintf(prefix, sizeof prefix, " (%s-%u): ", fatal ? "FATAL" : "WARNING", stid); print_str(prefix); } if (data->expr) { print_str("Assertion failure at "); } else { print_str("Code should not have been reached at "); } print_str(data->file); print_str(":"); print_str(print_number(line_buf, sizeof line_buf, data->line)); if (data->expr) { print_str(": \""); print_str(data->expr); print_str("\""); } print_str("\n"); flush_err_str(); if (log_stdout_is_distinct()) flush_str(STDOUT_FILENO); }
/** * Log message. */ void gl_logv(const char *domain, GLogLevelFlags flags, const char *fmt, va_list args) { static str_t *msg[THREAD_MAX]; static bool logging[THREAD_MAX]; unsigned stid = thread_small_id(); G_IGNORE_PUSH(-Wformat-nonliteral); /* s_minilogv() call below */ if (logging[stid]) { s_minilogv(flags | G_LOG_FLAG_RECURSION, FALSE, fmt, args); return; } G_IGNORE_POP; /* * This call is thread-unsafe by construction, and supposed to be called * only from the main thread. This is why it's OK to have a global * ``logging'' variable. */ logging[stid] = TRUE; if G_UNLIKELY(NULL == msg[stid]) msg[stid] = str_new_not_leaking(0); str_vprintf(msg[stid], fmt, args); if (handler_cb != NULL) (*handler_cb)(domain, flags, str_2c(msg[stid]), handler_data); else s_minilog(flags, "%s", str_2c(msg[stid])); logging[stid] = FALSE; }
/** * @note For maximum safety this is kept signal-safe, so that we can * even use assertions in signal handlers. See also: * http://www.opengroup.org/onlinepubs/009695399/functions/xsh_chap02_04.html */ static G_GNUC_COLD void assertion_message(const assertion_data * const data, int fatal) { char line_buf[ULONG_DEC_BUFLEN]; char time_buf[CRASH_TIME_BUFLEN]; char prefix[UINT_DEC_BUFLEN + CONST_STRLEN(" (WARNING-): ")]; unsigned stid, line; bool assertion; DECLARE_STR(16); crash_time(time_buf, sizeof time_buf); stid = thread_small_id(); /* * When an assertion failed in some thread, things are likely to break in * all the other threads and we want to avoid a cascade of failures being * reported. We suspend after computing the crash time, in case we were * not suspended due to a fatal error. */ thread_check_suspended(); print_str(time_buf); if (0 == stid) { print_str(fatal ? " (FATAL): " : " (WARNING): "); } else { str_bprintf(prefix, sizeof prefix, " (%s-%u): ", fatal ? "FATAL" : "WARNING", stid); print_str(prefix); } /* * If the FAST_ASSERT_NOT_REACHED bit is set in the line number, * then it does not indicate an assertion failure but rather that * we reached a point in the code that should never have been reached. * --RAM, 2013-10-28 */ line = data->line & ~FAST_ASSERT_NOT_REACHED; assertion = line == data->line; if (assertion) { print_str("Assertion failure at "); } else { print_str("Code should not have been reached in "); print_str(data->expr); /* Routine name */ print_str("() at "); } print_str(data->file); print_str(":"); print_str(PRINT_NUMBER(line_buf, line)); if (assertion) { print_str(": \""); print_str(data->expr); print_str("\""); } print_str("\n"); flush_err_str_atomic(); if (log_stdout_is_distinct()) flush_str_atomic(STDOUT_FILENO); }
/** * Perform specify operation on the address. * * @param addr the address we're querying * @param op the operation to perform * * @return NULL on failure, an address whose interpretation is op-specific * otherwise. */ static const void * dl_util_query(const void *addr, enum dl_addr_op op) { if G_UNLIKELY(!dl_util_inited) dl_util_init(); #ifdef HAS_DLADDR { static Dl_info info; static const void *last_addr; int stid = thread_small_id(); /* * Cache results for a given address. This will help our stack * pretty-printing code which is going to gather the various * items at different times instead of doing one dladdr() call. * For a given stack item, we may therefore face various calls * for the same address. * * The rationale is that we may want to use different routines on * another platform without dladdr() some days and therefore we wish * to hide the existence of dladdr() and rather provide higher-level * services like dl_util_get_base(). */ if (addr != last_addr) { signal_handler_t old_sigsegv; int ret; ZERO(&info); /* * Protect against segmentation faults in dladdr(). * * We use signal_catch() instead of signal_set() because we * don't need extra information about the fault context. */ old_sigsegv = signal_catch(SIGSEGV, dl_util_got_signal); if (Sigsetjmp(dl_util_env[stid], TRUE)) { last_addr = NULL; return NULL; } ret = dladdr(deconstify_pointer(addr), &info); signal_set(SIGSEGV, old_sigsegv); if (0 == ret) { last_addr = NULL; return NULL; } last_addr = addr; } switch (op) { case DL_ADDR_GET_BASE: return info.dli_fbase; case DL_ADDR_GET_NAME: return info.dli_sname; case DL_ADDR_GET_PATH: return info.dli_fname; case DL_ADDR_GET_START: return info.dli_saddr; } } g_assert_not_reached(); #else /* !HAS_DLADDR */ (void) addr; (void) op; return NULL; #endif /* HAS_DLADDR */ }