Beispiel #1
0
GError *
sqlx_cache_open_and_lock_base(sqlx_cache_t *cache, const hashstr_t *hname,
		gint *result)
{
	gint bd;
	GError *err = NULL;
	sqlx_base_t *base = NULL;

	EXTRA_ASSERT(cache != NULL);
	EXTRA_ASSERT(hname != NULL);
	EXTRA_ASSERT(result != NULL);

	gint64 start = oio_ext_monotonic_time();
	gint64 deadline = DEFAULT_CACHE_OPEN_TIMEOUT;
	if (cache->open_timeout > 0)
		deadline = cache->open_timeout;
	GRID_TRACE2("%s(%p,%s,%p) delay = %"G_GINT64_FORMAT, __FUNCTION__,
			(void*)cache, hname ? hashstr_str(hname) : "NULL",
			(void*)result, deadline);
	deadline += start;

	g_mutex_lock(&cache->lock);
	cache->used = TRUE;
retry:

	bd = sqlx_lookup_id(cache, hname);
	if (bd < 0) {
		if (!(err = sqlx_base_reserve(cache, hname, &base))) {
			bd = base->index;
			*result = base->index;
			sqlx_base_debug("OPEN", base);
		}
		else {
			GRID_DEBUG("No base available for [%s] (%d %s)",
					hashstr_str(hname), err->code, err->message);
			if (sqlx_expire_first_idle_base(cache, 0) >= 0) {
				g_clear_error(&err);
				goto retry;
			}
		}
	}
	else {
		base = GET(cache, bd);
		gint64 now = oio_ext_monotonic_time ();

		if (now > deadline) {
			err = NEWERROR (CODE_UNAVAILABLE,
					"DB busy (after %"G_GINT64_FORMAT" ms)",
					(now - start) / G_TIME_SPAN_MILLISECOND);
		} else switch (base->status) {

			case SQLX_BASE_FREE:
				EXTRA_ASSERT(base->count_open == 0);
				EXTRA_ASSERT(base->owner == NULL);
				GRID_ERROR("free base referenced");
				g_assert_not_reached();
				break;

			case SQLX_BASE_IDLE:
			case SQLX_BASE_IDLE_HOT:
				EXTRA_ASSERT(base->count_open == 0);
				EXTRA_ASSERT(base->owner == NULL);
				sqlx_base_move_to_list(cache, base, SQLX_BASE_USED);
				base->count_open ++;
				base->owner = g_thread_self();
				*result = base->index;
				break;

			case SQLX_BASE_USED:
				EXTRA_ASSERT(base->count_open > 0);
				EXTRA_ASSERT(base->owner != NULL);
				if (base->owner != g_thread_self()) {
					GRID_DEBUG("Base [%s] in use by another thread (%X), waiting...",
							hashstr_str(hname), oio_log_thread_id(base->owner));
					/* The lock is held by another thread/request.
					   XXX(jfs): do not use 'now' because it can be a fake clock */
					g_cond_wait_until(base->cond, &cache->lock,
							g_get_monotonic_time() + oio_cache_period_cond_wait);
					goto retry;
				}
				base->owner = g_thread_self();
				base->count_open ++;
				*result = base->index;
				break;

			case SQLX_BASE_CLOSING:
				EXTRA_ASSERT(base->owner != NULL);
				/* Just wait for a notification then retry
				   XXX(jfs): do not use 'now' because it can be a fake clock */
				g_cond_wait_until(base->cond, &cache->lock,
						g_get_monotonic_time() + oio_cache_period_cond_wait);
				goto retry;
		}
	}

	if (base) {
		if (!err) {
			sqlx_base_debug(__FUNCTION__, base);
			EXTRA_ASSERT(base->owner == g_thread_self());
			EXTRA_ASSERT(base->count_open > 0);
		}
		g_cond_signal(base->cond);
	}
	g_mutex_unlock(&cache->lock);
	return err;
}
Beispiel #2
0
GError *
sqlx_cache_open_and_lock_base(sqlx_cache_t *cache, const hashstr_t *hname,
		gint *result)
{
	gint bd;
	GError *err = NULL;
	sqlx_base_t *base = NULL;

	GRID_TRACE2("%s(%p,%s,%p)", __FUNCTION__, (void*)cache,
			hname ? hashstr_str(hname) : "NULL", (void*)result);
	EXTRA_ASSERT(cache != NULL);
	EXTRA_ASSERT(hname != NULL);
	EXTRA_ASSERT(result != NULL);

	gint64 deadline = g_get_monotonic_time();
	if (cache->open_timeout >= 0) {
		deadline += cache->open_timeout * G_TIME_SPAN_MILLISECOND;
	} else {
		deadline += 5 * G_TIME_SPAN_MINUTE;
	}

	g_mutex_lock(&cache->lock);
	cache->used = TRUE;
retry:
	bd = sqlx_lookup_id(cache, hname);
	if (bd < 0) {
		if (!(err = sqlx_base_reserve(cache, hname, &base))) {
			bd = base->index;
			*result = base->index;
			sqlx_base_debug("OPEN", base);
		}
		else {
			GRID_DEBUG("No base available for [%s] (%d %s)",
					hashstr_str(hname), err->code, err->message);
			if (sqlx_expire_first_idle_base(cache, NULL) >= 0) {
				g_clear_error(&err);
				goto retry;
			}
		}
	}
	else {
		base = GET(cache, bd);
		switch (base->status) {

			case SQLX_BASE_FREE:
				EXTRA_ASSERT(base->count_open == 0);
				EXTRA_ASSERT(base->owner == NULL);
				GRID_ERROR("free base referenced");
				g_assert_not_reached();
				break;

			case SQLX_BASE_IDLE:
			case SQLX_BASE_IDLE_HOT:
				EXTRA_ASSERT(base->count_open == 0);
				EXTRA_ASSERT(base->owner == NULL);
				sqlx_base_move_to_list(cache, base, SQLX_BASE_USED);
				base->count_open ++;
				base->owner = g_thread_self();
				*result = base->index;
				break;

			case SQLX_BASE_USED:
				EXTRA_ASSERT(base->count_open > 0);
				EXTRA_ASSERT(base->owner != NULL);
				if (base->owner != g_thread_self()) {
					GRID_DEBUG("Base [%s] in use by another thread (%X), waiting...",
							hashstr_str(hname), oio_log_thread_id(base->owner));
					// The lock is held by another thread/request
					if (g_cond_wait_until(base->cond, &cache->lock, deadline)) {
						GRID_DEBUG("Retrying to open [%s]", hashstr_str(hname));
						goto retry;
					} else {
						if (cache->open_timeout > 0) {
							err = NEWERROR(CODE_UNAVAILABLE,
								"database currently in use by another request"
								" (we waited %ldms)",
								cache->open_timeout);
						} else {
							err = NEWERROR(CODE_UNAVAILABLE,
								"database currently in use by another request");
						}
						GRID_DEBUG("failed to open base: "
								"in use by another request (thread %X)",
								oio_log_thread_id(base->owner));
						break;
					}
				}
				base->owner = g_thread_self();
				base->count_open ++;
				*result = base->index;
				break;

			case SQLX_BASE_CLOSING:
				EXTRA_ASSERT(base->owner != NULL);
				// Just wait for a notification then retry
				if (g_cond_wait_until(base->cond, &cache->lock, deadline))
					goto retry;
				else {
					err = NEWERROR(CODE_UNAVAILABLE,
							"Database stuck in closing state");
					break;
				}
		}
	}

	if (base) {
		if (!err) {
			sqlx_base_debug(__FUNCTION__, base);
			EXTRA_ASSERT(base->owner == g_thread_self());
			EXTRA_ASSERT(base->count_open > 0);
		}
		g_cond_signal(base->cond);
	}
	g_mutex_unlock(&cache->lock);
	return err;
}
Beispiel #3
0
GError *
sqlx_cache_open_and_lock_base(sqlx_cache_t *cache, const hashstr_t *hname,
                              gint *result)
{
    gint bd;
    GError *err = NULL;
    sqlx_base_t *base = NULL;
    GTimeVal *deadline = g_alloca(sizeof(GTimeVal));

    GRID_TRACE2("%s(%p,%s,%p)", __FUNCTION__, (void*)cache,
                hname ? hashstr_str(hname) : "NULL", (void*)result);
    EXTRA_ASSERT(cache != NULL);
    EXTRA_ASSERT(hname != NULL);
    EXTRA_ASSERT(result != NULL);

    if (cache->open_timeout >= 0) {
        g_get_current_time(deadline);
        g_time_val_add(deadline, cache->open_timeout * 1000);
    } else {
        // wait forever
        deadline = NULL;
    }

    g_mutex_lock(cache->lock);
    cache->used = TRUE;
retry:
    bd = sqlx_lookup_id(cache, hname);
    if (bd < 0) {
        if (!(err = sqlx_base_reserve(cache, hname, &base))) {
            bd = base->index;
            *result = base->index;
            sqlx_base_debug("OPEN", base);
        }
        else {
            GRID_DEBUG("No base available for [%s] (%d %s)",
                       hashstr_str(hname), err->code, err->message);
            if (sqlx_expire_first_idle_base(cache, NULL) >= 0) {
                g_clear_error(&err);
                goto retry;
            }
        }
    }
    else {
        base = GET(cache, bd);
        switch (base->status) {

        case SQLX_BASE_FREE:
            EXTRA_ASSERT(base->count_open == 0);
            EXTRA_ASSERT(base->owner == NULL);
            GRID_ERROR("free base referenced");
            g_assert_not_reached();
            break;

        case SQLX_BASE_IDLE:
        case SQLX_BASE_IDLE_HOT:
            EXTRA_ASSERT(base->count_open == 0);
            EXTRA_ASSERT(base->owner == NULL);
            sqlx_base_move_to_list(cache, base, SQLX_BASE_USED);
            base->count_open ++;
            base->owner = g_thread_self();
            *result = base->index;
            break;

        case SQLX_BASE_USED:
            EXTRA_ASSERT(base->count_open > 0);
            EXTRA_ASSERT(base->owner != NULL);
            if (base->owner != g_thread_self()) {
                // The lock is held by another thread/request
                GRID_DEBUG("Base [%s] in use by another thread (%X), waiting...",
                           hashstr_str(hname), compute_thread_id(base->owner));

                /* This is to avoid server thread starvation,
                 * due to all threads waiting on the same base. */
                if (cache->max_waiting > 0 &&
                        base->count_waiting >= cache->max_waiting) {
                    err = NEWERROR(CODE_UNAVAILABLE,
                                   "database currently in use by another request, "
                                   "and %d others threads already waiting",
                                   base->count_waiting);
                    break;
                }
                base->count_waiting++;

                if (g_cond_timed_wait(base->cond, cache->lock, deadline)) {
                    // Thread was woken up before deadline
                    GRID_DEBUG("Retrying to open [%s]", hashstr_str(hname));
                    base->count_waiting--;
                    goto retry;
                } else {
                    // Deadline has been reached
                    base->count_waiting--;
                    if (cache->open_timeout > 0) {
                        err = NEWERROR(CODE_UNAVAILABLE,
                                       "database currently in use by another request"
                                       " (we waited %ldms)",
                                       cache->open_timeout);
                    } else {
                        err = NEWERROR(CODE_UNAVAILABLE,
                                       "database currently in use by another request");
                    }
                    GRID_DEBUG("failed to open base: "
                               "in use by another request (thread %X)",
                               compute_thread_id(base->owner));
                    break;
                }
            }
            base->owner = g_thread_self();
            base->count_open ++;
            *result = base->index;
            break;

        case SQLX_BASE_CLOSING:
            EXTRA_ASSERT(base->owner != NULL);
            // Just wait for a notification then retry
            if (g_cond_timed_wait(base->cond, cache->lock, deadline))
                goto retry;
            else {
                err = NEWERROR(CODE_UNAVAILABLE,
                               "Database stuck in closing state");
                break;
            }
        }
    }

    if (base) {
        if (!err) {
            sqlx_base_debug(__FUNCTION__, base);
            EXTRA_ASSERT(base->owner == g_thread_self());
            EXTRA_ASSERT(base->count_open > 0);
        }
        g_cond_signal(base->cond);
    }
    g_mutex_unlock(cache->lock);
    return err;
}