text: /*-
   * See the file LICENSE for redistribution information.
   *
   * Copyright (c) 1997-2009 Oracle.  All rights reserved.
   *
   * $Id$
   */
  
  #include <sys/types.h>
  
  #include <errno.h>
  #include <iostream>
  #include <stdlib.h>
  #include <string.h>
  
  #include <db_cxx.h>
  
  using std::cin;
  using std::cout;
  using std::cerr;
  
  const char *progname = "LockExample";				// Program name.
  
  //
  // An example of a program using DBLock and related classes.
  //
  class LockExample : public DbEnv
  {
  public:
  	void run();
  	int error_code() { return (ecode); }
  
  	LockExample(const char *home, u_int32_t maxlocks, int do_unlink);
  
  private:
  	static const char FileName[];
  	int ecode;
  
  	// no need for copy and assignment
  	LockExample(const LockExample &);
  	void operator = (const LockExample &);
  };
  
  static int usage();          // forward
  
  int
  main(int argc, char *argv[])
  {
  	const char *home;
  	int do_unlink;
  	u_int32_t maxlocks;
  	int i;
  
  	home = "TESTDIR";
  	maxlocks = 0;
  	do_unlink = 0;
  	for (int argnum = 1; argnum < argc; ++argnum) {
  		if (strcmp(argv[argnum], "-h") == 0) {
  			if (++argnum >= argc)
  				return (usage());
  			home = argv[argnum];
  		}
  		else if (strcmp(argv[argnum], "-m") == 0) {
  			if (++argnum >= argc)
  				return (usage());
  			if ((i = atoi(argv[argnum])) <= 0)
  				return (usage());
  			maxlocks = (u_int32_t)i;  /* XXX: possible overflow. */
  		}
  		else if (strcmp(argv[argnum], "-u") == 0) {
  			do_unlink = 1;
  		}
  		else {
  			return (usage());
  		}
  	}
  
  	try {
  		int ecode;
  
  		if (do_unlink) {
  			// Create an environment that immediately
  			// removes all files.
  			LockExample tmp(home, maxlocks, do_unlink);
  			if ((ecode = tmp.error_code()) != 0)
  				return (ecode);
  		}
  
  		LockExample app(home, maxlocks, do_unlink);
  		if ((ecode = app.error_code()) != 0)
  			return (ecode);
  		app.run();
  		app.close(0);
  		return (EXIT_SUCCESS);
  	}
  	catch (DbException &dbe) {
  		cerr << "LockExample: " << dbe.what() << "\n";
  		return (EXIT_FAILURE);
  	}
  }
  
  LockExample::LockExample(const char *home, u_int32_t maxlocks, int do_unlink)
  :	DbEnv(0)
  ,	ecode(0)
  {
  	int ret;
  
  	if (do_unlink) {
  		if ((ret = remove(home, DB_FORCE)) != 0) {
  			cerr << progname << ": DbEnv::remove: "
  			     << strerror(errno) << "\n";
  			ecode = EXIT_FAILURE;
  		}
  	}
  	else {
  		set_error_stream(&cerr);
  		set_errpfx("LockExample");
  		if (maxlocks != 0)
  			set_lk_max_locks(maxlocks);
  		open(home, DB_CREATE | DB_INIT_LOCK, 0);
  	}
  }
  
  void LockExample::run()
  {
  	long held;
  	size_t len;
  	u_int32_t locker;
  	int did_get, ret;
  	DbLock *locks = 0;
  	int lockcount = 0;
  	int lockid = 0;
  	char objbuf[1024];
  
  	//
  	// Accept lock requests.
  	//
  	lock_id(&locker);
  	for (held = 0;;) {
  		cout << "Operation get/release [get]> ";
  		cout.flush();
  
  		char opbuf[16];
  		cin.getline(opbuf, sizeof(opbuf));
  		if (cin.eof())
  			break;
  		if ((len = strlen(opbuf)) <= 1 || strcmp(opbuf, "get") == 0) {
  			// Acquire a lock.
  			cout << "input object (text string) to lock> ";
  			cout.flush();
  			cin.getline(objbuf, sizeof(objbuf));
  			if (cin.eof())
  				break;
  			if ((len = strlen(objbuf)) <= 0)
  				continue;
  
  			char lockbuf[16];
  			do {
  				cout << "lock type read/write [read]> ";
  				cout.flush();
  				cin.getline(lockbuf, sizeof(lockbuf));
  				if (cin.eof())
  					break;
  				len = strlen(lockbuf);
  			} while (len >= 1 &&
  				 strcmp(lockbuf, "read") != 0 &&
  				 strcmp(lockbuf, "write") != 0);
  
  			db_lockmode_t lock_type;
  			if (len <= 1 || strcmp(lockbuf, "read") == 0)
  				lock_type = DB_LOCK_READ;
  			else
  				lock_type = DB_LOCK_WRITE;
  
  			Dbt dbt(objbuf, (u_int32_t)strlen(objbuf));
  
  			DbLock lock;
  			ret = lock_get(locker, DB_LOCK_NOWAIT, &dbt,
  				       lock_type, &lock);
  			did_get = 1;
  			lockid = lockcount++;
  			if (locks == NULL) {
  				locks = new DbLock[1];
  			}
  			else {
  				DbLock *newlocks = new DbLock[lockcount];
  				for (int lockno = 0;
  				    lockno < lockid; lockno++) {
  					newlocks[lockno] = locks[lockno];
  				}
  				delete locks;
  				locks = newlocks;
  			}
  			locks[lockid] = lock;
  		} else {
  			// Release a lock.
  			do {
  				cout << "input lock to release> ";
  				cout.flush();
  				cin.getline(objbuf, sizeof(objbuf));
  				if (cin.eof())
  					break;
  			} while ((len = strlen(objbuf)) <= 0);
  			lockid = strtol(objbuf, NULL, 16);
  			if (lockid < 0 || lockid >= lockcount) {
  				cout << "Lock #" << lockid << " out of range\n";
  				continue;
  			}
  			DbLock lock = locks[lockid];
  			ret = lock_put(&lock);
  			did_get = 0;
  		}
  
  		switch (ret) {
  		case 0:
  			cout << "Lock #" << lockid << " "
  			     <<  (did_get ? "granted" : "released")
  			     << "\n";
  			held += did_get ? 1 : -1;
  			break;
  		case DB_LOCK_NOTGRANTED:
  			cout << "Lock not granted\n";
  			break;
  		case DB_LOCK_DEADLOCK:
  			cerr << "LockExample: lock_"
  			     << (did_get ? "get" : "put")
  			     << ": " << "returned DEADLOCK";
  			break;
  		default:
  			cerr << "LockExample: lock_get: %s",
  				strerror(errno);
  		}
  	}
  	cout << "\n";
  	cout << "Closing lock region " << held << " locks held\n";
  	if (locks != 0)
  		delete locks;
  }
  
  static int
  usage()
  {
  	cerr << "usage: LockExample [-u] [-h home] [-m maxlocks]\n";
  	return (EXIT_FAILURE);
  }
  