1/*-
2 * See the file LICENSE for redistribution information.
3 *
4 * Copyright (c) 1996,2008 Oracle.  All rights reserved.
5 *
6 * $Id: lock_timer.c,v 12.16 2008/01/08 20:58:41 bostic Exp $
7 */
8
9#include "db_config.h"
10
11#include "db_int.h"
12#include "dbinc/lock.h"
13
14/*
15 * __lock_set_timeout --
16 *	Set timeout values in shared memory.
17 *
18 * This is called from the transaction system.  We either set the time that
19 * this transaction expires or the amount of time a lock for this transaction
20 * is permitted to wait.
21 *
22 * PUBLIC: int __lock_set_timeout __P((ENV *,
23 * PUBLIC:      DB_LOCKER *, db_timeout_t, u_int32_t));
24 */
25int
26__lock_set_timeout(env, locker, timeout, op)
27	ENV *env;
28	DB_LOCKER *locker;
29	db_timeout_t timeout;
30	u_int32_t op;
31{
32	int ret;
33
34	if (locker == NULL)
35		return (0);
36	LOCK_REGION_LOCK(env);
37	ret = __lock_set_timeout_internal(env, locker, timeout, op);
38	LOCK_REGION_UNLOCK(env);
39	return (ret);
40}
41
42/*
43 * __lock_set_timeout_internal
44 *		-- set timeout values in shared memory.
45 *
46 * This is the internal version called from the lock system.  We either set
47 * the time that this transaction expires or the amount of time that a lock
48 * for this transaction is permitted to wait.
49 *
50 * PUBLIC: int __lock_set_timeout_internal
51 * PUBLIC:     __P((ENV *, DB_LOCKER *, db_timeout_t, u_int32_t));
52 */
53int
54__lock_set_timeout_internal(env, sh_locker, timeout, op)
55	ENV *env;
56	DB_LOCKER *sh_locker;
57	db_timeout_t timeout;
58	u_int32_t op;
59{
60	DB_LOCKREGION *region;
61	region = env->lk_handle->reginfo.primary;
62
63	if (op == DB_SET_TXN_TIMEOUT) {
64		if (timeout == 0)
65			timespecclear(&sh_locker->tx_expire);
66		else
67			__lock_expires(env, &sh_locker->tx_expire, timeout);
68	} else if (op == DB_SET_LOCK_TIMEOUT) {
69		sh_locker->lk_timeout = timeout;
70		F_SET(sh_locker, DB_LOCKER_TIMEOUT);
71	} else if (op == DB_SET_TXN_NOW) {
72		timespecclear(&sh_locker->tx_expire);
73		__lock_expires(env, &sh_locker->tx_expire, 0);
74		sh_locker->lk_expire = sh_locker->tx_expire;
75		if (!timespecisset(&region->next_timeout) ||
76		    timespeccmp(
77			&region->next_timeout, &sh_locker->lk_expire, >))
78			region->next_timeout = sh_locker->lk_expire;
79	} else
80		return (EINVAL);
81
82	return (0);
83}
84
85/*
86 * __lock_inherit_timeout
87 *		-- inherit timeout values from parent locker.
88 * This is called from the transaction system.  This will
89 * return EINVAL if the parent does not exist or did not
90 * have a current txn timeout set.
91 *
92 * PUBLIC: int __lock_inherit_timeout __P((ENV *, DB_LOCKER *, DB_LOCKER *));
93 */
94int
95__lock_inherit_timeout(env, parent, locker)
96	ENV *env;
97	DB_LOCKER *parent, *locker;
98{
99	int ret;
100
101	ret = 0;
102	LOCK_REGION_LOCK(env);
103
104	/*
105	 * If the parent is not there yet, thats ok.  If it
106	 * does not have any timouts set, then avoid creating
107	 * the child locker at this point.
108	 */
109	if (parent == NULL ||
110	    (timespecisset(&parent->tx_expire) &&
111	    !F_ISSET(parent, DB_LOCKER_TIMEOUT))) {
112		ret = EINVAL;
113		goto err;
114	}
115
116	locker->tx_expire = parent->tx_expire;
117
118	if (F_ISSET(parent, DB_LOCKER_TIMEOUT)) {
119		locker->lk_timeout = parent->lk_timeout;
120		F_SET(locker, DB_LOCKER_TIMEOUT);
121		if (!timespecisset(&parent->tx_expire))
122			ret = EINVAL;
123	}
124
125err:	LOCK_REGION_UNLOCK(env);
126	return (ret);
127}
128
129/*
130 * __lock_expires --
131 *	Set the expire time given the time to live.
132 *
133 * PUBLIC: void __lock_expires __P((ENV *, db_timespec *, db_timeout_t));
134 */
135void
136__lock_expires(env, timespecp, timeout)
137	ENV *env;
138	db_timespec *timespecp;
139	db_timeout_t timeout;
140{
141	db_timespec v;
142
143	/*
144	 * If timespecp is set then it contains "now".  This avoids repeated
145	 * system calls to get the time.
146	 */
147	if (!timespecisset(timespecp))
148		__os_gettime(env, timespecp, 1);
149
150	/* Convert the microsecond timeout argument to a timespec. */
151	DB_TIMEOUT_TO_TIMESPEC(timeout, &v);
152
153	/* Add the timeout to "now". */
154	timespecadd(timespecp, &v);
155}
156
157/*
158 * __lock_expired -- determine if a lock has expired.
159 *
160 * PUBLIC: int __lock_expired __P((ENV *, db_timespec *, db_timespec *));
161 */
162int
163__lock_expired(env, now, timespecp)
164	ENV *env;
165	db_timespec *now, *timespecp;
166{
167	if (!timespecisset(timespecp))
168		return (0);
169
170	if (!timespecisset(now))
171		__os_gettime(env, now, 1);
172
173	return (timespeccmp(now, timespecp, >=));
174}
175