// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*-
// Copyright (c) 2001-2007 International Computer Science Institute
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software")
// to deal in the Software without restriction, subject to the conditions
// listed in the XORP LICENSE file. These conditions include: you must
// preserve this copyright notice, and you cannot mention the copyright
// holders in advertising related to the Software without their permission.
// The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
// notice is a summary of the XORP LICENSE file; the license in that file is
// legally binding.
#ident "$XORP: xorp/libxorp/transaction.cc,v 1.9 2007/02/16 22:46:27 pavlin Exp $"
#include <assert.h>
#include "libxorp/debug.h"
#include "libxorp/random.h"
#include "libxorp/eventloop.hh"
#include "libxorp/transaction.hh"
/* ------------------------------------------------------------------------- */
/* Inline TransactionManager::Transaction methods */
inline void
TransactionManager::Transaction::add(const Operation& op)
{
_ops.push_back(op);
_op_count++;
defer_timeout();
}
inline void
TransactionManager::Transaction::commit()
{
//
// Unschedule timeout timer, defense against a calls to
// EventLoop.run() in whacky places.
//
_timeout_timer.unschedule();
while (_ops.empty() == false) {
// Copy front of list, not a biggie data is refcnt'ed
Operation op = _ops.front();
// Erase item from list so if error occurs we don't have to
// reference anything on list again; i.e., error handler could
// abort this transaction...
_ops.erase(_ops.begin());
_op_count--;
//
// Deref ref_ptr to get reference to operation
//
TransactionOperation& top = *(op.get());
bool success = top.dispatch();
//
// Give manager a chance to deal with success / error
//
_mgr.operation_result(success, top);
}
}
inline void
TransactionManager::Transaction::flush()
{
while (_ops.empty() == false) {
_ops.erase(_ops.begin());
_op_count--;
}
}
inline void
TransactionManager::Transaction::defer_timeout()
{
uint32_t timeout_ms = _mgr.timeout_ms();
if (timeout_ms)
_timeout_timer.schedule_after_ms(timeout_ms);
}
inline void
TransactionManager::Transaction::cancel_timeout()
{
_timeout_timer.clear();
}
/* ------------------------------------------------------------------------- */
/* Transaction Manager methods */
void
TransactionManager::crank_tid()
{
//
// This would be very bad if number of pending transactions is large.
// In practice the bad case should be well outside bounds of this code.
//
_next_tid++;
do {
_next_tid += (random() & 0xfffff);
} while (_transactions.find(_next_tid) != _transactions.end());
}
bool
TransactionManager::start(uint32_t& new_tid)
{
if (pending() == max_pending())
return false;
crank_tid();
if (timeout_ms()) {
XorpTimer t = _e.new_oneoff_after_ms(
timeout_ms(),
callback(this,&TransactionManager::timeout, _next_tid)
);
_transactions.insert(TransactionDB::value_type(
_next_tid, Transaction(*this, t))
);
} else {
_transactions.insert(TransactionDB::value_type(_next_tid,
Transaction(*this)));
}
new_tid = _next_tid;
return true;
}
bool
TransactionManager::abort(uint32_t tid)
{
TransactionDB::iterator i = _transactions.find(tid);
if (i == _transactions.end())
return false;
_transactions.erase(i);
return true;
}
void
TransactionManager::pre_commit(uint32_t /* tid */)
{}
void
TransactionManager::post_commit(uint32_t /* tid */)
{}
void
TransactionManager::operation_result(bool /* success */,
const TransactionOperation& /* op */)
{}
bool
TransactionManager::flush(uint32_t tid)
{
TransactionDB::iterator i = _transactions.find(tid);
if (i == _transactions.end()) {
return false;
}
Transaction& t = i->second;
t.flush();
return true;
}
bool
TransactionManager::commit(uint32_t tid)
{
if (_transactions.find(tid) == _transactions.end()) {
return false;
}
pre_commit(tid);
//
// Check user did not do abort transaction in pre_commit(). This
// comes at a cost since we've already done find(), but is better than
// dereferencing an invalid iterator. We could disallow/prevent
// abort by swapping transaction operations onto a temporary list
// and deleting the transaction, but there are cases where abort
// might be desirable in pre_commit and the double lookup is not
// that expensive.
//
TransactionDB::iterator i = _transactions.find(tid);
if (i == _transactions.end()) {
return false;
}
Transaction& t = i->second;
//
// Sanity check, perhaps not the most appropriate place for this
//
assert(t.operations().size() == t.size());
//
// Commit all operations in queue
//
t.commit();
//
// Sanity check, perhaps not the most appropriate place for this
//
assert(t.operations().size() == t.size());
//
// Erase transaction
//
_transactions.erase(i);
post_commit(tid);
return true;
}
bool
TransactionManager::add(uint32_t tid, const Operation& op)
{
TransactionDB::iterator i = _transactions.find(tid);
if (i == _transactions.end())
return false;
i->second.add(op);
return true;
}
bool
TransactionManager::retrieve_size(uint32_t tid, uint32_t& count) const
{
TransactionDB::const_iterator i = _transactions.find(tid);
if (i == _transactions.end())
return false;
count = i->second.size();
return true;
}
void
TransactionManager::timeout(uint32_t tid)
{
TransactionDB::iterator i = _transactions.find(tid);
if (i == _transactions.end())
return;
debug_msg("Timing out transaction id %u\n", XORP_UINT_CAST(tid));
_transactions.erase(i);
}
syntax highlighted by Code2HTML, v. 0.9.1