1/*-
2 * See the file LICENSE for redistribution information.
3 *
4 * Copyright (c) 1997,2008 Oracle.  All rights reserved.
5 *
6 * $Id: LockExample.cpp,v 12.6 2008/01/08 20:58:26 bostic Exp $
7 */
8
9#include <sys/types.h>
10
11#include <errno.h>
12#include <iostream>
13#include <stdlib.h>
14#include <string.h>
15
16#include <db_cxx.h>
17
18using std::cin;
19using std::cout;
20using std::cerr;
21
22const char *progname = "LockExample";				// Program name.
23
24//
25// An example of a program using DBLock and related classes.
26//
27class LockExample : public DbEnv
28{
29public:
30	void run();
31	int error_code() { return (ecode); }
32
33	LockExample(const char *home, u_int32_t maxlocks, int do_unlink);
34
35private:
36	static const char FileName[];
37	int ecode;
38
39	// no need for copy and assignment
40	LockExample(const LockExample &);
41	void operator = (const LockExample &);
42};
43
44static int usage();          // forward
45
46int
47main(int argc, char *argv[])
48{
49	const char *home;
50	int do_unlink;
51	u_int32_t maxlocks;
52	int i;
53
54	home = "TESTDIR";
55	maxlocks = 0;
56	do_unlink = 0;
57	for (int argnum = 1; argnum < argc; ++argnum) {
58		if (strcmp(argv[argnum], "-h") == 0) {
59			if (++argnum >= argc)
60				return (usage());
61			home = argv[argnum];
62		}
63		else if (strcmp(argv[argnum], "-m") == 0) {
64			if (++argnum >= argc)
65				return (usage());
66			if ((i = atoi(argv[argnum])) <= 0)
67				return (usage());
68			maxlocks = (u_int32_t)i;  /* XXX: possible overflow. */
69		}
70		else if (strcmp(argv[argnum], "-u") == 0) {
71			do_unlink = 1;
72		}
73		else {
74			return (usage());
75		}
76	}
77
78	try {
79		int ecode;
80
81		if (do_unlink) {
82			// Create an environment that immediately
83			// removes all files.
84			LockExample tmp(home, maxlocks, do_unlink);
85			if ((ecode = tmp.error_code()) != 0)
86				return (ecode);
87		}
88
89		LockExample app(home, maxlocks, do_unlink);
90		if ((ecode = app.error_code()) != 0)
91			return (ecode);
92		app.run();
93		app.close(0);
94		return (EXIT_SUCCESS);
95	}
96	catch (DbException &dbe) {
97		cerr << "LockExample: " << dbe.what() << "\n";
98		return (EXIT_FAILURE);
99	}
100}
101
102LockExample::LockExample(const char *home, u_int32_t maxlocks, int do_unlink)
103:	DbEnv(0)
104,	ecode(0)
105{
106	int ret;
107
108	if (do_unlink) {
109		if ((ret = remove(home, DB_FORCE)) != 0) {
110			cerr << progname << ": DbEnv::remove: "
111			     << strerror(errno) << "\n";
112			ecode = EXIT_FAILURE;
113		}
114	}
115	else {
116		set_error_stream(&cerr);
117		set_errpfx("LockExample");
118		if (maxlocks != 0)
119			set_lk_max_locks(maxlocks);
120		open(home, DB_CREATE | DB_INIT_LOCK, 0);
121	}
122}
123
124void LockExample::run()
125{
126	long held;
127	size_t len;
128	u_int32_t locker;
129	int did_get, ret;
130	DbLock *locks = 0;
131	int lockcount = 0;
132	int lockid = 0;
133	char objbuf[1024];
134
135	//
136	// Accept lock requests.
137	//
138	lock_id(&locker);
139	for (held = 0;;) {
140		cout << "Operation get/release [get]> ";
141		cout.flush();
142
143		char opbuf[16];
144		cin.getline(opbuf, sizeof(opbuf));
145		if (cin.eof())
146			break;
147		if ((len = strlen(opbuf)) <= 1 || strcmp(opbuf, "get") == 0) {
148			// Acquire a lock.
149			cout << "input object (text string) to lock> ";
150			cout.flush();
151			cin.getline(objbuf, sizeof(objbuf));
152			if (cin.eof())
153				break;
154			if ((len = strlen(objbuf)) <= 0)
155				continue;
156
157			char lockbuf[16];
158			do {
159				cout << "lock type read/write [read]> ";
160				cout.flush();
161				cin.getline(lockbuf, sizeof(lockbuf));
162				if (cin.eof())
163					break;
164				len = strlen(lockbuf);
165			} while (len >= 1 &&
166				 strcmp(lockbuf, "read") != 0 &&
167				 strcmp(lockbuf, "write") != 0);
168
169			db_lockmode_t lock_type;
170			if (len <= 1 || strcmp(lockbuf, "read") == 0)
171				lock_type = DB_LOCK_READ;
172			else
173				lock_type = DB_LOCK_WRITE;
174
175			Dbt dbt(objbuf, (u_int32_t)strlen(objbuf));
176
177			DbLock lock;
178			ret = lock_get(locker, DB_LOCK_NOWAIT, &dbt,
179				       lock_type, &lock);
180			did_get = 1;
181			lockid = lockcount++;
182			if (locks == NULL) {
183				locks = new DbLock[1];
184			}
185			else {
186				DbLock *newlocks = new DbLock[lockcount];
187				for (int lockno = 0;
188				    lockno < lockid; lockno++) {
189					newlocks[lockno] = locks[lockno];
190				}
191				delete locks;
192				locks = newlocks;
193			}
194			locks[lockid] = lock;
195		} else {
196			// Release a lock.
197			do {
198				cout << "input lock to release> ";
199				cout.flush();
200				cin.getline(objbuf, sizeof(objbuf));
201				if (cin.eof())
202					break;
203			} while ((len = strlen(objbuf)) <= 0);
204			lockid = strtol(objbuf, NULL, 16);
205			if (lockid < 0 || lockid >= lockcount) {
206				cout << "Lock #" << lockid << " out of range\n";
207				continue;
208			}
209			DbLock lock = locks[lockid];
210			ret = lock_put(&lock);
211			did_get = 0;
212		}
213
214		switch (ret) {
215		case 0:
216			cout << "Lock #" << lockid << " "
217			     <<  (did_get ? "granted" : "released")
218			     << "\n";
219			held += did_get ? 1 : -1;
220			break;
221		case DB_LOCK_NOTGRANTED:
222			cout << "Lock not granted\n";
223			break;
224		case DB_LOCK_DEADLOCK:
225			cerr << "LockExample: lock_"
226			     << (did_get ? "get" : "put")
227			     << ": " << "returned DEADLOCK";
228			break;
229		default:
230			cerr << "LockExample: lock_get: %s",
231				strerror(errno);
232		}
233	}
234	cout << "\n";
235	cout << "Closing lock region " << held << " locks held\n";
236	if (locks != 0)
237		delete locks;
238}
239
240static int
241usage()
242{
243	cerr << "usage: LockExample [-u] [-h home] [-m maxlocks]\n";
244	return (EXIT_FAILURE);
245}
246