diff options
| author | Rajith Muditha Attapattu <rajith@apache.org> | 2008-12-19 19:34:45 +0000 |
|---|---|---|
| committer | Rajith Muditha Attapattu <rajith@apache.org> | 2008-12-19 19:34:45 +0000 |
| commit | 38cde902ffe68eac8ffb0884bcc9c7bfa98c02ac (patch) | |
| tree | 3599403c0c9690898f1e336c009a5564c587c732 /RC5/cpp/src/qpid/sys | |
| parent | a8960649bcd365ef70a5de7812f5910222388a6d (diff) | |
| download | qpid-python-38cde902ffe68eac8ffb0884bcc9c7bfa98c02ac.tar.gz | |
Tagging RC5 for M4 release
git-svn-id: https://svn.apache.org/repos/asf/qpid/tags/M4@728121 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'RC5/cpp/src/qpid/sys')
131 files changed, 15004 insertions, 0 deletions
diff --git a/RC5/cpp/src/qpid/sys/ActivityTimer.h b/RC5/cpp/src/qpid/sys/ActivityTimer.h new file mode 100644 index 0000000000..d49e16bc4f --- /dev/null +++ b/RC5/cpp/src/qpid/sys/ActivityTimer.h @@ -0,0 +1,106 @@ +#ifndef QPID_SYS_ACTIVITYTIMER_H +#define QPID_SYS_ACTIVITYTIMER_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/sys/Time.h" +#include "qpid/sys/Thread.h" +#include <boost/current_function.hpp> +#include <stdio.h> + +namespace qpid { +namespace sys { + +/** + * Measures and reports time spent in a particular segment of code. + * This is real time so it includes time blocked/sleeping as well as time on CPU. + * + * Intended to be used via the QPID_ACTIVITY_TIMER macro for profiling + * during development & debugging + */ +class ActivityTimer +{ + public: + + struct Stat { // Must be a POD + uint64_t total, count; + void sample(uint64_t value) { total += value; ++count; } + uint64_t mean() { return count ? total/count : 0; } + void reset() { total = count = 0; } + }; + + struct Data { // Must be a POD + uint64_t start, entered; + Stat active; + + void reset() { + start = entered = 0; + active.reset(); + } + + void enter(uint64_t now) { + entered=now; + if (!start) start = Duration(now); + } + + void exit(uint64_t now) { + active.sample(now - entered); + } + }; + + ActivityTimer(Data& d, const char* fn, const char* file, int line, uint64_t reportInterval) : data(d) { + uint64_t now = Duration(qpid::sys::now()); + if (data.start) { + interval = now-data.start; + if (interval > reportInterval) + report(fn, file, line); + } + data.enter(now); + } + + ~ActivityTimer() { + data.exit(Duration(now())); + } + + private: + Data& data; + uint64_t interval; + + void report(const char* fn, const char* file, int line) { + long rate = (data.active.count*TIME_SEC)/interval; + double percent = (data.active.total*100.0)/interval; + printf("%s:%d: TIMER %ld/sec %f%% [%lu] %s\n", + file, line, rate, percent, Thread::current().id(), fn); + data.reset(); + } +}; + +}} // namespace qpid::sys + +/** Measures time between the point of declaration and the end of the innermost enclosing scope. + * Can only have one in a given scope. + */ +#define ACTIVITY_TIMER(REPORT_INTERVAL_SECS) \ + static __thread ::qpid::sys::ActivityTimer::Data qpid__ActivityTimerData__ = { 0, 0, { 0,0 }}; \ + ::qpid::sys::ActivityTimer qpid__ActivityTimerInstance__(qpid__ActivityTimerData__, BOOST_CURRENT_FUNCTION, __FILE__, __LINE__, 2*::qpid::sys::TIME_SEC) + +#endif /*!QPID_SYS_ACTIVITYTIMER_H*/ diff --git a/RC5/cpp/src/qpid/sys/AggregateOutput.cpp b/RC5/cpp/src/qpid/sys/AggregateOutput.cpp new file mode 100644 index 0000000000..fa6901d3e4 --- /dev/null +++ b/RC5/cpp/src/qpid/sys/AggregateOutput.cpp @@ -0,0 +1,73 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/sys/AggregateOutput.h" +#include "qpid/log/Statement.h" +#include <algorithm> + +namespace qpid { +namespace sys { + +void AggregateOutput::activateOutput() { control.activateOutput(); } + +void AggregateOutput::giveReadCredit(int32_t credit) { control.giveReadCredit(credit); } + +bool AggregateOutput::hasOutput() { + for (TaskList::const_iterator i = tasks.begin(); i != tasks.end(); ++i) + if ((*i)->hasOutput()) return true; + return false; +} + +bool AggregateOutput::doOutput() +{ + bool result = false; + if (!tasks.empty()) { + if (next >= tasks.size()) next = next % tasks.size(); + + size_t start = next; + //loop until a task generated some output + while (!result) { + result = tasks[next++]->doOutput(); + if (tasks.empty()) break; + if (next >= tasks.size()) next = next % tasks.size(); + if (start == next) break; + } + } + return result; +} + +void AggregateOutput::addOutputTask(OutputTask* t) +{ + tasks.push_back(t); +} + +void AggregateOutput::removeOutputTask(OutputTask* t) +{ + TaskList::iterator i = std::find(tasks.begin(), tasks.end(), t); + if (i != tasks.end()) tasks.erase(i); +} + +void AggregateOutput::removeAll() +{ + tasks.clear(); +} + +}} // namespace qpid::sys diff --git a/RC5/cpp/src/qpid/sys/AggregateOutput.h b/RC5/cpp/src/qpid/sys/AggregateOutput.h new file mode 100644 index 0000000000..1cda4456b4 --- /dev/null +++ b/RC5/cpp/src/qpid/sys/AggregateOutput.h @@ -0,0 +1,65 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _AggregateOutput_ +#define _AggregateOutput_ + +#include "Mutex.h" +#include "OutputControl.h" +#include "OutputTask.h" + +#include <algorithm> +#include <vector> + +namespace qpid { +namespace sys { + + class AggregateOutput : public OutputTask, public OutputControl + { + typedef std::vector<OutputTask*> TaskList; + + TaskList tasks; + size_t next; + OutputControl& control; + + public: + AggregateOutput(OutputControl& c) : next(0), control(c) {}; + //this may be called on any thread + void activateOutput(); + void giveReadCredit(int32_t); + + //all the following will be called on the same thread + bool doOutput(); + bool hasOutput(); + void addOutputTask(OutputTask* t); + void removeOutputTask(OutputTask* t); + void removeAll(); + + /** Apply f to each OutputTask* in the tasks list */ + template <class F> void eachOutput(F f) { + std::for_each(tasks.begin(), tasks.end(), f); + } + }; + +} +} + + +#endif diff --git a/RC5/cpp/src/qpid/sys/AsynchIO.h b/RC5/cpp/src/qpid/sys/AsynchIO.h new file mode 100644 index 0000000000..68e441349a --- /dev/null +++ b/RC5/cpp/src/qpid/sys/AsynchIO.h @@ -0,0 +1,152 @@ +#ifndef _sys_AsynchIO +#define _sys_AsynchIO +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +// @@TODO: TAKE THIS OUT... SHould be in posix version. +#include "DispatchHandle.h" + +#include <boost/function.hpp> +#include <deque> + +namespace qpid { +namespace sys { + +class Socket; + +/* + * Asynchronous acceptor: accepts connections then does a callback with the + * accepted fd + */ +class AsynchAcceptorPrivate; +class AsynchAcceptor { +public: + typedef boost::function1<void, const Socket&> Callback; + +private: + AsynchAcceptorPrivate* impl; + +public: + AsynchAcceptor(const Socket& s, Callback callback); + ~AsynchAcceptor(); + void start(Poller::shared_ptr poller); +}; + +/* + * Asynchronous connector: starts the process of initiating a connection and + * invokes a callback when completed or failed. + */ +class AsynchConnector { +public: + typedef boost::function1<void, const Socket&> ConnectedCallback; + typedef boost::function2<void, int, std::string> FailedCallback; + + // Call create() to allocate a new AsynchConnector object with the + // specified poller, addressing, and callbacks. + // This method is implemented in platform-specific code to + // create a correctly typed object. The platform code also manages + // deletes. To correctly manage heaps when needed, the allocate and + // delete should both be done from the same class/library. + static AsynchConnector* create(const Socket& s, + Poller::shared_ptr poller, + std::string hostname, + uint16_t port, + ConnectedCallback connCb, + FailedCallback failCb = 0); + +protected: + AsynchConnector() {} + virtual ~AsynchConnector() {} +}; + +struct AsynchIOBufferBase { + char* const bytes; + const int32_t byteCount; + int32_t dataStart; + int32_t dataCount; + + AsynchIOBufferBase(char* const b, const int32_t s) : + bytes(b), + byteCount(s), + dataStart(0), + dataCount(0) + {} + + virtual ~AsynchIOBufferBase() + {} +}; + +/* + * Asychronous reader/writer: + * Reader accepts buffers to read into; reads into the provided buffers + * and then does a callback with the buffer and amount read. Optionally it + * can callback when there is something to read but no buffer to read it into. + * + * Writer accepts a buffer and queues it for writing; can also be given + * a callback for when writing is "idle" (ie fd is writable, but nothing + * to write). + */ +class AsynchIO { +public: + typedef AsynchIOBufferBase BufferBase; + + typedef boost::function2<bool, AsynchIO&, BufferBase*> ReadCallback; + typedef boost::function1<void, AsynchIO&> EofCallback; + typedef boost::function1<void, AsynchIO&> DisconnectCallback; + typedef boost::function2<void, AsynchIO&, const Socket&> ClosedCallback; + typedef boost::function1<void, AsynchIO&> BuffersEmptyCallback; + typedef boost::function1<void, AsynchIO&> IdleCallback; + + // Call create() to allocate a new AsynchIO object with the specified + // callbacks. This method is implemented in platform-specific code to + // create a correctly typed object. The platform code also manages + // deletes. To correctly manage heaps when needed, the allocate and + // delete should both be done from the same class/library. + static AsynchIO* create(const Socket& s, + ReadCallback rCb, + EofCallback eofCb, + DisconnectCallback disCb, + ClosedCallback cCb = 0, + BuffersEmptyCallback eCb = 0, + IdleCallback iCb = 0); +public: + virtual void queueForDeletion() = 0; + + virtual void start(Poller::shared_ptr poller) = 0; + virtual void queueReadBuffer(BufferBase* buff) = 0; + virtual void unread(BufferBase* buff) = 0; + virtual void queueWrite(BufferBase* buff) = 0; + virtual void notifyPendingWrite() = 0; + virtual void queueWriteClose() = 0; + virtual bool writeQueueEmpty() = 0; + virtual void startReading() = 0; + virtual BufferBase* getQueuedBuffer() = 0; + +protected: + // Derived class manages lifetime; must be constructed using the + // static create() method. Deletes not allowed from outside. + AsynchIO() {} + virtual ~AsynchIO() {} +}; + +}} + +#endif // _sys_AsynchIO diff --git a/RC5/cpp/src/qpid/sys/AsynchIOHandler.cpp b/RC5/cpp/src/qpid/sys/AsynchIOHandler.cpp new file mode 100644 index 0000000000..83b6329889 --- /dev/null +++ b/RC5/cpp/src/qpid/sys/AsynchIOHandler.cpp @@ -0,0 +1,199 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "AsynchIOHandler.h" +#include "qpid/sys/AsynchIO.h" +#include "qpid/sys/Socket.h" +#include "qpid/framing/AMQP_HighestVersion.h" +#include "qpid/framing/ProtocolInitiation.h" +#include "qpid/log/Statement.h" + +namespace qpid { +namespace sys { + +// Buffer definition +struct Buff : public AsynchIO::BufferBase { + Buff() : + AsynchIO::BufferBase(new char[65536], 65536) + {} + ~Buff() + { delete [] bytes;} +}; + +AsynchIOHandler::AsynchIOHandler(std::string id, ConnectionCodec::Factory* f) : + identifier(id), + aio(0), + factory(f), + codec(0), + readError(false), + isClient(false), + readCredit(InfiniteCredit) +{} + +AsynchIOHandler::~AsynchIOHandler() { + if (codec) + codec->closed(); + delete codec; +} + +void AsynchIOHandler::init(AsynchIO* a, int numBuffs) { + aio = a; + + // Give connection some buffers to use + for (int i = 0; i < numBuffs; i++) { + aio->queueReadBuffer(new Buff); + } +} + +void AsynchIOHandler::write(const framing::ProtocolInitiation& data) +{ + QPID_LOG(debug, "SENT [" << identifier << "] INIT(" << data << ")"); + AsynchIO::BufferBase* buff = aio->getQueuedBuffer(); + if (!buff) + buff = new Buff; + framing::Buffer out(buff->bytes, buff->byteCount); + data.encode(out); + buff->dataCount = data.encodedSize(); + aio->queueWrite(buff); +} + +void AsynchIOHandler::activateOutput() { + aio->notifyPendingWrite(); +} + +// Input side +void AsynchIOHandler::giveReadCredit(int32_t credit) { + // Check whether we started in the don't about credit state + if (readCredit.boolCompareAndSwap(InfiniteCredit, credit)) + return; + else if (readCredit.fetchAndAdd(credit) != 0) + return; + // Lock and retest credit to make sure we don't race with decreasing credit + ScopedLock<Mutex> l(creditLock); + assert(readCredit.get() >= 0); + if (readCredit.get() != 0) + aio->startReading(); +} + +bool AsynchIOHandler::readbuff(AsynchIO& , AsynchIO::BufferBase* buff) { + if (readError) { + return false; + } + size_t decoded = 0; + if (codec) { // Already initiated + try { + decoded = codec->decode(buff->bytes+buff->dataStart, buff->dataCount); + }catch(const std::exception& e){ + QPID_LOG(error, e.what()); + readError = true; + aio->queueWriteClose(); + } + }else{ + framing::Buffer in(buff->bytes+buff->dataStart, buff->dataCount); + framing::ProtocolInitiation protocolInit; + if (protocolInit.decode(in)) { + decoded = in.getPosition(); + QPID_LOG(debug, "RECV [" << identifier << "] INIT(" << protocolInit << ")"); + try { + codec = factory->create(protocolInit.getVersion(), *this, identifier); + if (!codec) { + //TODO: may still want to revise this... + //send valid version header & close connection. + write(framing::ProtocolInitiation(framing::highestProtocolVersion)); + readError = true; + aio->queueWriteClose(); + } + } catch (const std::exception& e) { + QPID_LOG(error, e.what()); + readError = true; + aio->queueWriteClose(); + } + } + } + // TODO: unreading needs to go away, and when we can cope + // with multiple sub-buffers in the general buffer scheme, it will + if (decoded != size_t(buff->dataCount)) { + // Adjust buffer for used bytes and then "unread them" + buff->dataStart += decoded; + buff->dataCount -= decoded; + aio->unread(buff); + } else { + // Give whole buffer back to aio subsystem + aio->queueReadBuffer(buff); + } + // Check here for read credit + if (readCredit.get() != InfiniteCredit) { + if (--readCredit == 0) { + // Lock and retest credit to make sure we don't race with increasing credit + ScopedLock<Mutex> l(creditLock); + assert(readCredit.get() >= 0); + if (readCredit.get() == 0) + return false; + } + } + return true; +} + +void AsynchIOHandler::eof(AsynchIO&) { + QPID_LOG(debug, "DISCONNECTED [" << identifier << "]"); + if (codec) codec->closed(); + aio->queueWriteClose(); +} + +void AsynchIOHandler::closedSocket(AsynchIO&, const Socket& s) { + // If we closed with data still to send log a warning + if (!aio->writeQueueEmpty()) { + QPID_LOG(warning, "CLOSING [" << identifier << "] unsent data (probably due to client disconnect)"); + } + delete &s; + aio->queueForDeletion(); + delete this; +} + +void AsynchIOHandler::disconnect(AsynchIO& a) { + // treat the same as eof + eof(a); +} + +// Notifications +void AsynchIOHandler::nobuffs(AsynchIO&) { +} + +void AsynchIOHandler::idle(AsynchIO&){ + if (isClient && codec == 0) { + codec = factory->create(*this, identifier); + write(framing::ProtocolInitiation(codec->getVersion())); + return; + } + if (codec == 0) return; + if (codec->canEncode()) { + // Try and get a queued buffer if not then construct new one + AsynchIO::BufferBase* buff = aio->getQueuedBuffer(); + if (!buff) buff = new Buff; + size_t encoded=codec->encode(buff->bytes, buff->byteCount); + buff->dataCount = encoded; + aio->queueWrite(buff); + } + if (codec->isClosed()) + aio->queueWriteClose(); +} + +}} // namespace qpid::sys diff --git a/RC5/cpp/src/qpid/sys/AsynchIOHandler.h b/RC5/cpp/src/qpid/sys/AsynchIOHandler.h new file mode 100644 index 0000000000..fa020fbce4 --- /dev/null +++ b/RC5/cpp/src/qpid/sys/AsynchIOHandler.h @@ -0,0 +1,79 @@ +#ifndef _sys_AsynchIOHandler_h +#define _sys_AsynchIOHandler_h +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "OutputControl.h" +#include "ConnectionCodec.h" +#include "AtomicValue.h" +#include "Mutex.h" + +namespace qpid { + +namespace framing { + class ProtocolInitiation; +} + +namespace sys { + +class AsynchIO; +struct AsynchIOBufferBase; +class Socket; + +class AsynchIOHandler : public OutputControl { + std::string identifier; + AsynchIO* aio; + ConnectionCodec::Factory* factory; + ConnectionCodec* codec; + bool readError; + bool isClient; + AtomicValue<int32_t> readCredit; + static const int32_t InfiniteCredit = -1; + Mutex creditLock; + + void write(const framing::ProtocolInitiation&); + + public: + AsynchIOHandler(std::string id, ConnectionCodec::Factory* f); + ~AsynchIOHandler(); + void init(AsynchIO* a, int numBuffs); + + void setClient() { isClient = true; } + + // Output side + void close(); + void activateOutput(); + void giveReadCredit(int32_t credit); + + // Input side + bool readbuff(AsynchIO& aio, AsynchIOBufferBase* buff); + void eof(AsynchIO& aio); + void disconnect(AsynchIO& aio); + + // Notifications + void nobuffs(AsynchIO& aio); + void idle(AsynchIO& aio); + void closedSocket(AsynchIO& aio, const Socket& s); +}; + +}} // namespace qpid::sys + +#endif // _sys_AsynchIOHandler_h diff --git a/RC5/cpp/src/qpid/sys/AtomicCount.h b/RC5/cpp/src/qpid/sys/AtomicCount.h new file mode 100644 index 0000000000..d598b49427 --- /dev/null +++ b/RC5/cpp/src/qpid/sys/AtomicCount.h @@ -0,0 +1,52 @@ +#ifndef _posix_AtomicCount_h +#define _posix_AtomicCount_h + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <boost/detail/atomic_count.hpp> +#include "ScopedIncrement.h" + +namespace qpid { +namespace sys { + +/** + * Atomic counter. + */ +class AtomicCount { + public: + typedef ::qpid::sys::ScopedDecrement<AtomicCount> ScopedDecrement; + typedef ::qpid::sys::ScopedIncrement<AtomicCount> ScopedIncrement; + + AtomicCount(long value = 0) : count(value) {} + + void operator++() { ++count ; } + + long operator--() { return --count; } + + operator long() const { return count; } + + private: + boost::detail::atomic_count count; +}; + + +}} + + +#endif // _posix_AtomicCount_h diff --git a/RC5/cpp/src/qpid/sys/AtomicValue.h b/RC5/cpp/src/qpid/sys/AtomicValue.h new file mode 100644 index 0000000000..6e90eafead --- /dev/null +++ b/RC5/cpp/src/qpid/sys/AtomicValue.h @@ -0,0 +1,34 @@ +#ifndef QPID_SYS_ATOMICVALUE_H +#define QPID_SYS_ATOMICVALUE_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#if defined( __GNUC__ ) && __GNUC__ >= 4 && ( defined( __i686__ ) || defined( __x86_64__ ) ) +// Use the Gnu C built-in atomic operations if compiling with gcc on a suitable platform. +#include "qpid/sys/AtomicValue_gcc.h" + +#else +// Fall-back to mutex locked operations if we don't have atomic ops. +#include "qpid/sys/AtomicValue_mutex.h" +#endif + +#endif /*!QPID_SYS_ATOMICVALUE_GCC_H*/ diff --git a/RC5/cpp/src/qpid/sys/AtomicValue_gcc.h b/RC5/cpp/src/qpid/sys/AtomicValue_gcc.h new file mode 100644 index 0000000000..d022b07c1d --- /dev/null +++ b/RC5/cpp/src/qpid/sys/AtomicValue_gcc.h @@ -0,0 +1,68 @@ +#ifndef QPID_SYS_ATOMICVALUE_GCC_H +#define QPID_SYS_ATOMICVALUE_GCC_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#if !defined(QPID_SYS_ATOMICVALUE_H) +#error "This file should only be included via AtomicValue.h." +#endif + +namespace qpid { +namespace sys { + +/** + * Atomic value of type T. T must be an integral type of size 1,2,4 or 8 bytes. + * All operations are atomic and preform a full memory barrier unless otherwise noted. + */ +template <class T> +class AtomicValue +{ + public: + AtomicValue(T init=0) : value(init) {} + + // Update and return new value. + inline T operator+=(T n) { return __sync_add_and_fetch(&value, n); } + inline T operator-=(T n) { return __sync_sub_and_fetch(&value, n); } + inline T operator++() { return *this += 1; } + inline T operator--() { return *this -= 1; } + + // Update and return old value. + inline T fetchAndAdd(T n) { return __sync_fetch_and_add(&value, n); } + inline T fetchAndSub(T n) { return __sync_fetch_and_sub(&value, n); } + inline T operator++(int) { return fetchAndAdd(1); } + inline T operator--(int) { return fetchAndSub(1); } + + /** If current value == testval then set to newval. Returns the old value. */ + T valueCompareAndSwap(T testval, T newval) { return __sync_val_compare_and_swap(&value, testval, newval); } + + /** If current value == testval then set to newval. Returns true if the swap was performed. */ + bool boolCompareAndSwap(T testval, T newval) { return __sync_bool_compare_and_swap(&value, testval, newval); } + + T get() const { return const_cast<AtomicValue<T>*>(this)->fetchAndAdd(static_cast<T>(0)); } + + private: + T value; +}; + +}} // namespace qpid::sys + +#endif /*!QPID_SYS_ATOMICVALUE_GCC_H*/ diff --git a/RC5/cpp/src/qpid/sys/AtomicValue_mutex.h b/RC5/cpp/src/qpid/sys/AtomicValue_mutex.h new file mode 100644 index 0000000000..e4d433e7f5 --- /dev/null +++ b/RC5/cpp/src/qpid/sys/AtomicValue_mutex.h @@ -0,0 +1,83 @@ +#ifndef QPID_SYS_ATOMICVALUE_MUTEX_H +#define QPID_SYS_ATOMICVALUE_MUTEX_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#if !defined(QPID_SYS_ATOMICVALUE_H) +#error "This file should only be included via AtomicValue.h." +#endif + +#include "qpid/sys/Mutex.h" + +namespace qpid { +namespace sys { + +/** + * Atomic value of type T. T must be an integral type of size 1,2,4 or 8 bytes. + * All operations are atomic and preform a full memory barrier unless otherwise noted. + */ +template <class T> +class AtomicValue +{ + public: + AtomicValue(T init=0) : value(init) {} + + // Update and return new value. + inline T operator+=(T n) { Lock l(lock); return value += n; } + inline T operator-=(T n) { Lock l(lock); return value -= n; } + inline T operator++() { return *this += 1; } + inline T operator--() { return *this -= 1; } + + // Update and return old value. + inline T fetchAndAdd(T n) { Lock l(lock); T old=value; value += n; return old; } + inline T fetchAndSub(T n) { Lock l(lock); T old=value; value -= n; return old; } + inline T operator++(int) { return fetchAndAdd(1); } + inline T operator--(int) { return fetchAndSub(1); } + + AtomicValue& operator=(T newval) { Lock l(lock); value = newval; return *this; } + + /** If current value == testval then set to newval. Returns the old value. */ + T valueCompareAndSwap(T testval, T newval) { + Lock l(lock); + T old=value; + if (value == testval) value = newval; + return old; + } + + /** If current value == testval then set to newval. Returns true if the swap was performed. */ + bool boolCompareAndSwap(T testval, T newval) { + Lock l(lock); + if (value == testval) { value = newval; return true; } + return false; + } + + T get() const { Lock l(lock); return value; } + + private: + typedef Mutex::ScopedLock Lock; + T value; + mutable Mutex lock; +}; + +}} // namespace qpid::sys + +#endif /*!QPID_SYS_ATOMICVALUE_MUTEX_H*/ diff --git a/RC5/cpp/src/qpid/sys/BlockingQueue.h b/RC5/cpp/src/qpid/sys/BlockingQueue.h new file mode 100644 index 0000000000..a05a10d811 --- /dev/null +++ b/RC5/cpp/src/qpid/sys/BlockingQueue.h @@ -0,0 +1,125 @@ +#ifndef QPID_SYS_BLOCKINGQUEUE_H +#define QPID_SYS_BLOCKINGQUEUE_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "Waitable.h" + +#include <queue> + +namespace qpid { +namespace sys { + +/** + * A simple blocking queue template + */ +template <class T> +class BlockingQueue +{ + mutable sys::Waitable waitable; + std::queue<T> queue; + +public: + BlockingQueue() {} + ~BlockingQueue() { close(); } + + /** Pop from the queue, block up to timeout if empty. + *@param result Set to value popped from queue. + *@param timeout Defaults to infinite. + *@return true if result was set, false if queue empty after timeout. + */ + bool pop(T& result, Duration timeout=TIME_INFINITE) { + Mutex::ScopedLock l(waitable); + { + Waitable::ScopedWait w(waitable); + if (timeout == TIME_INFINITE) { + while (queue.empty()) waitable.wait(); + } else { + AbsTime deadline(now(),timeout); + while (queue.empty() && deadline > now()) waitable.wait(deadline); + } + } + if (queue.empty()) return false; + result = queue.front(); + queue.pop(); + if (!queue.empty()) + waitable.notify(); // Notify another waiter. + return true; + } + + T pop(Duration timeout=TIME_INFINITE) { + T result; + bool ok = pop(result, timeout); + if (!ok) + throw Exception("Timed out waiting on a blocking queue"); + return result; + } + + /** Push a value onto the queue. + * Note it is not an error to push onto a closed queue. + */ + void push(const T& t) { + Mutex::ScopedLock l(waitable); + queue.push(t); + waitable.notify(); // Notify a waiter. + } + + /** + * Close the queue. + *@ex exception to throw to waiting threads. ClosedException by default. + */ + void close(const ExceptionHolder& ex=ExceptionHolder(new ClosedException())) + { + Mutex::ScopedLock l(waitable); + if (!waitable.hasException()) { + waitable.setException(ex); + waitable.notifyAll(); + waitable.waitWaiters(); // Ensure no threads are still waiting. + } + } + + /** Open a closed queue. */ + void open() { + Mutex::ScopedLock l(waitable); + waitable.resetException(); + } + + bool isClosed() const { + Mutex::ScopedLock l(waitable); + return waitable.hasException(); + } + + bool empty() const { + Mutex::ScopedLock l(waitable); + return queue.empty(); + } + size_t size() const { + Mutex::ScopedLock l(waitable); + return queue.size(); + } +}; + +}} + + + +#endif /*!QPID_SYS_BLOCKINGQUEUE_H*/ diff --git a/RC5/cpp/src/qpid/sys/Condition.h b/RC5/cpp/src/qpid/sys/Condition.h new file mode 100644 index 0000000000..fe0e3a1c71 --- /dev/null +++ b/RC5/cpp/src/qpid/sys/Condition.h @@ -0,0 +1,33 @@ +#ifndef _sys_Condition_h +#define _sys_Condition_h + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#ifdef USE_APR_PLATFORM +#include "apr/Condition.h" +#elif defined (_WIN32) +#include "windows/Condition.h" +#else +#include "posix/Condition.h" +#endif + +#endif /*!_sys_Condition_h*/ diff --git a/RC5/cpp/src/qpid/sys/ConnectionCodec.h b/RC5/cpp/src/qpid/sys/ConnectionCodec.h new file mode 100644 index 0000000000..b1b047d2cc --- /dev/null +++ b/RC5/cpp/src/qpid/sys/ConnectionCodec.h @@ -0,0 +1,78 @@ +#ifndef QPID_SYS_CONNECTION_CODEC_H +#define QPID_SYS_CONNECTION_CODEC_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/framing/ProtocolVersion.h" + +namespace qpid { + +namespace sys { + +class InputHandlerFactory; +class OutputControl; + +/** + * Interface of coder/decoder for a connection of a specific protocol + * version. + */ +class ConnectionCodec { + public: + virtual ~ConnectionCodec() {} + + /** Decode from buffer, return number of bytes decoded. + * @return may be less than size if there was incomplete + * data at the end of the buffer. + */ + virtual size_t decode(const char* buffer, size_t size) = 0; + + + /** Encode into buffer, return number of bytes encoded */ + virtual size_t encode(const char* buffer, size_t size) = 0; + + /** Return true if we have data to encode */ + virtual bool canEncode() = 0; + + /** Network connection was closed from other end. */ + virtual void closed() = 0; + + virtual bool isClosed() const = 0; + + virtual framing::ProtocolVersion getVersion() const = 0; + + struct Factory { + virtual ~Factory() {} + + /** Return 0 if version unknown */ + virtual ConnectionCodec* create( + framing::ProtocolVersion, OutputControl&, const std::string& id + ) = 0; + + /** Return "preferred" codec for outbound connections. */ + virtual ConnectionCodec* create( + OutputControl&, const std::string& id + ) = 0; + }; +}; + +}} // namespace qpid::sys + +#endif /*!QPID_SYS_CONNECTION_CODEC_H*/ diff --git a/RC5/cpp/src/qpid/sys/ConnectionInputHandler.h b/RC5/cpp/src/qpid/sys/ConnectionInputHandler.h new file mode 100644 index 0000000000..9a5b9f75a5 --- /dev/null +++ b/RC5/cpp/src/qpid/sys/ConnectionInputHandler.h @@ -0,0 +1,44 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _ConnectionInputHandler_ +#define _ConnectionInputHandler_ + +#include "qpid/framing/InputHandler.h" +#include "OutputTask.h" +#include "TimeoutHandler.h" + +namespace qpid { +namespace sys { + + class ConnectionInputHandler : + public qpid::framing::InputHandler, + public TimeoutHandler, public OutputTask + { + public: + + virtual void closed() = 0; + }; + +} +} + + +#endif diff --git a/RC5/cpp/src/qpid/sys/ConnectionInputHandlerFactory.h b/RC5/cpp/src/qpid/sys/ConnectionInputHandlerFactory.h new file mode 100644 index 0000000000..9bb7e13686 --- /dev/null +++ b/RC5/cpp/src/qpid/sys/ConnectionInputHandlerFactory.h @@ -0,0 +1,54 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _ConnectionInputHandlerFactory_ +#define _ConnectionInputHandlerFactory_ + +#include <boost/noncopyable.hpp> +#include <string> + +namespace qpid { +namespace sys { + +class ConnectionOutputHandler; +class ConnectionInputHandler; + +/** + * Callback interface used by the Acceptor to + * create a ConnectionInputHandler for each new connection. + */ +class ConnectionInputHandlerFactory : private boost::noncopyable +{ + public: + /** + *@param out handler for connection output. + *@param id identify the connection for management purposes. + */ + virtual ConnectionInputHandler* create(ConnectionOutputHandler* out, + const std::string& id, + bool isClient) = 0; + + virtual ~ConnectionInputHandlerFactory(){} +}; + +}} + + +#endif diff --git a/RC5/cpp/src/qpid/sys/ConnectionOutputHandler.h b/RC5/cpp/src/qpid/sys/ConnectionOutputHandler.h new file mode 100644 index 0000000000..de0bef3630 --- /dev/null +++ b/RC5/cpp/src/qpid/sys/ConnectionOutputHandler.h @@ -0,0 +1,43 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _ConnectionOutputHandler_ +#define _ConnectionOutputHandler_ + +#include "qpid/framing/OutputHandler.h" +#include "OutputControl.h" + +namespace qpid { +namespace sys { + +/** + * Provides the output handler associated with a connection. + */ +class ConnectionOutputHandler : public virtual qpid::framing::OutputHandler, public OutputControl +{ + public: + virtual void close() = 0; + virtual size_t getBuffered() const { return 0; } +}; + +}} + + +#endif diff --git a/RC5/cpp/src/qpid/sys/ConnectionOutputHandlerPtr.h b/RC5/cpp/src/qpid/sys/ConnectionOutputHandlerPtr.h new file mode 100644 index 0000000000..df6de89982 --- /dev/null +++ b/RC5/cpp/src/qpid/sys/ConnectionOutputHandlerPtr.h @@ -0,0 +1,55 @@ +#ifndef QPID_SYS_CONNECTIONOUTPUTHANDLERPTR_H +#define QPID_SYS_CONNECTIONOUTPUTHANDLERPTR_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "ConnectionOutputHandler.h" + +namespace qpid { +namespace sys { + +/** + * A ConnectionOutputHandler that delegates to another + * ConnectionOutputHandler. Allows the "real" ConnectionOutputHandler + * to be changed modified without updating all the pointers/references + * using the ConnectionOutputHandlerPtr + */ +class ConnectionOutputHandlerPtr : public ConnectionOutputHandler +{ + public: + ConnectionOutputHandlerPtr(ConnectionOutputHandler* p) : next(p) { assert(next); } + void set(ConnectionOutputHandler* p) { next = p; assert(next); } + ConnectionOutputHandler* get() { return next; } + const ConnectionOutputHandler* get() const { return next; } + + void close() { next->close(); } + size_t getBuffered() const { return next->getBuffered(); } + void activateOutput() { next->activateOutput(); } + void giveReadCredit(int32_t credit) { next->giveReadCredit(credit); } + void send(framing::AMQFrame& f) { next->send(f); } + + private: + ConnectionOutputHandler* next; +}; +}} // namespace qpid::sys + +#endif /*!QPID_SYS_CONNECTIONOUTPUTHANDLERPTR_H*/ diff --git a/RC5/cpp/src/qpid/sys/CopyOnWriteArray.h b/RC5/cpp/src/qpid/sys/CopyOnWriteArray.h new file mode 100644 index 0000000000..c5bdcc0942 --- /dev/null +++ b/RC5/cpp/src/qpid/sys/CopyOnWriteArray.h @@ -0,0 +1,126 @@ +#ifndef QPID_SYS_COPYONWRITEARRAY_H +#define QPID_SYS_COPYONWRITEARRAY_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "Mutex.h" +#include <algorithm> +#include <vector> +#include <boost/shared_ptr.hpp> + +namespace qpid { +namespace sys { + +/** + * An array that copies on adding/removing element allowing lock-free + * iteration. + */ +template <class T> +class CopyOnWriteArray +{ +public: + typedef boost::shared_ptr<const std::vector<T> > ConstPtr; + + CopyOnWriteArray() {} + CopyOnWriteArray(const CopyOnWriteArray& c) : array(c.array) {} + + void add(T& t) + { + Mutex::ScopedLock l(lock); + ArrayPtr copy(array ? new std::vector<T>(*array) : new std::vector<T>()); + copy->push_back(t); + array = copy; + } + + bool remove(T& t) + { + Mutex::ScopedLock l(lock); + if (array && std::find(array->begin(), array->end(), t) != array->end()) { + ArrayPtr copy(new std::vector<T>(*array)); + copy->erase(std::find(copy->begin(), copy->end(), t)); + array = copy; + return true; + } else { + return false; + } + } + + template <class F> + bool add_unless(T& t, F f) + { + Mutex::ScopedLock l(lock); + if (array && find_if(array->begin(), array->end(), f) != array->end()) { + return false; + } else { + ArrayPtr copy(array ? new std::vector<T>(*array) : new std::vector<T>()); + copy->push_back(t); + array = copy; + return true; + } + } + + template <class F> + bool remove_if(F f) + { + Mutex::ScopedLock l(lock); + if (array && std::find_if(array->begin(), array->end(), f) != array->end()) { + ArrayPtr copy(new std::vector<T>(*array)); + copy->erase(std::remove_if(copy->begin(), copy->end(), f), copy->end()); + array = copy; + return true; + } + return false; + } + + template <class F> + F for_each(F f) + { + ArrayPtr a; + { + Mutex::ScopedLock l(lock); + a = array; + } + if (!a) return f; + return std::for_each(a->begin(), a->end(), f); + } + + ConstPtr snapshot() + { + ConstPtr a; + { + Mutex::ScopedLock l(lock); + a = array; + } + return a; + } + +private: + typedef boost::shared_ptr< std::vector<T> > ArrayPtr; + Mutex lock; + ArrayPtr array; +}; + +}} + + + +#endif /*!QPID_SYS_COPYONWRITEARRAY_H*/ diff --git a/RC5/cpp/src/qpid/sys/DeletionManager.h b/RC5/cpp/src/qpid/sys/DeletionManager.h new file mode 100644 index 0000000000..43154eb98e --- /dev/null +++ b/RC5/cpp/src/qpid/sys/DeletionManager.h @@ -0,0 +1,138 @@ +#ifndef _sys_DeletionManager_h +#define _sys_DeletionManager_h + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include <vector> +#include <algorithm> +#include <boost/shared_ptr.hpp> + +namespace qpid { +namespace sys { + +struct deleter +{ + template <typename T> + void operator()(T* ptr){ delete ptr;} +}; + +/** + * DeletionManager keeps track of handles that need to be deleted but may still be + * in use by one of the threads concurrently. + * + * The mode of operation is like this: + * - When we want to delete but we might still be using the handle we + * * Transfer ownership of the handle to this class + * * Mark the handle as (potentially) in use by every thread + * - Then subsequently at points where the thread code knows it isn't + * using any handles it declares that it is using no handles + * - When the last thread declares no use of a handle it automatically + * gets deleted by the shared_ptr implementation + * + * The class only has static members and data and so can only be used once for + * any particular handle type + */ +template <typename H> +class DeletionManager +{ +public: + // Mark every thread as using the handle - it will be deleted + // below after every thread marks the handle as unused + static void markForDeletion(H* handle) { + allThreadsStatuses.addHandle(shared_ptr(handle)); + } + + // Mark this thread is not using any handle - + // handles get deleted here when no one else + // is using them either + static void markAllUnusedInThisThread() { + static __thread ThreadStatus* threadStatus = 0; + + // Thread local vars can't be dynamically constructed so we need + // to check whether we've made it yet and construct it if not + // (no locking necessary for the check as it's thread local!) + if (!threadStatus) { + threadStatus = new ThreadStatus; + allThreadsStatuses.addThreadStatus(threadStatus); + } + + ScopedLock<Mutex> l(threadStatus->lock); + + // The actual deletions will happen here when all the shared_ptr + // ref counts hit 0 (that is when every thread marks the handle unused) + threadStatus->handles.clear(); + } + +private: + typedef boost::shared_ptr<H> shared_ptr; + + // In theory we know that we never need more handles than the number of + // threads runnning so we could use a fixed size array. However at this point + // in the code we don't have easy access to this information. + struct ThreadStatus + { + Mutex lock; + std::vector<shared_ptr> handles; + }; + + class AllThreadsStatuses + { + Mutex lock; + std::vector<ThreadStatus*> statuses; + + struct handleAdder + { + shared_ptr handle; + + handleAdder(shared_ptr h): handle(h) {} + + void operator()(ThreadStatus* ptr) { + ScopedLock<Mutex> l(ptr->lock); + ptr->handles.push_back(handle); + } + }; + + public: + // Need this to be able to do static initialisation + explicit AllThreadsStatuses(int) {} + + ~AllThreadsStatuses() { + ScopedLock<Mutex> l(lock); + std::for_each(statuses.begin(), statuses.end(), deleter()); + } + + void addThreadStatus(ThreadStatus* t) { + ScopedLock<Mutex> l(lock); + statuses.push_back(t); + } + + void addHandle(shared_ptr h) { + ScopedLock<Mutex> l(lock); + std::for_each(statuses.begin(), statuses.end(), handleAdder(h)); + } + }; + + static AllThreadsStatuses allThreadsStatuses; +}; + +}} +#endif // _sys_DeletionManager_h diff --git a/RC5/cpp/src/qpid/sys/DispatchHandle.cpp b/RC5/cpp/src/qpid/sys/DispatchHandle.cpp new file mode 100644 index 0000000000..4722fc0b8b --- /dev/null +++ b/RC5/cpp/src/qpid/sys/DispatchHandle.cpp @@ -0,0 +1,409 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "DispatchHandle.h" + +#include <boost/cast.hpp> + +#include <assert.h> + +namespace qpid { +namespace sys { + +DispatchHandle::~DispatchHandle() { + stopWatch(); +} + +void DispatchHandle::startWatch(Poller::shared_ptr poller0) { + bool r = readableCallback; + bool w = writableCallback; + + ScopedLock<Mutex> lock(stateLock); + assert(state == IDLE); + + // If no callbacks set then do nothing (that is what we were asked to do!) + // TODO: Maybe this should be an assert instead + if (!r && !w) { + state = INACTIVE; + return; + } + + Poller::Direction d = r ? + (w ? Poller::INOUT : Poller::INPUT) : + Poller::OUTPUT; + + poller = poller0; + poller->addFd(*this, d); + + state = r ? + (w ? ACTIVE_RW : ACTIVE_R) : + ACTIVE_W; +} + +void DispatchHandle::rewatch() { + bool r = readableCallback; + bool w = writableCallback; + + ScopedLock<Mutex> lock(stateLock); + switch(state) { + case IDLE: + case DELAYED_IDLE: + break; + case DELAYED_R: + case DELAYED_W: + case DELAYED_INACTIVE: + state = r ? + (w ? DELAYED_RW : DELAYED_R) : + DELAYED_W; + break; + case DELAYED_DELETE: + break; + case INACTIVE: + case ACTIVE_R: + case ACTIVE_W: { + assert(poller); + Poller::Direction d = r ? + (w ? Poller::INOUT : Poller::INPUT) : + Poller::OUTPUT; + poller->modFd(*this, d); + state = r ? + (w ? ACTIVE_RW : ACTIVE_R) : + ACTIVE_W; + break; + } + case DELAYED_RW: + case ACTIVE_RW: + // Don't need to do anything already waiting for readable/writable + break; + } +} + +void DispatchHandle::rewatchRead() { + if (!readableCallback) { + return; + } + + ScopedLock<Mutex> lock(stateLock); + switch(state) { + case IDLE: + case DELAYED_IDLE: + break; + case DELAYED_R: + case DELAYED_RW: + case DELAYED_DELETE: + break; + case DELAYED_W: + state = DELAYED_RW; + break; + case DELAYED_INACTIVE: + state = DELAYED_R; + break; + case ACTIVE_R: + case ACTIVE_RW: + // Nothing to do: already waiting for readable + break; + case INACTIVE: + assert(poller); + poller->modFd(*this, Poller::INPUT); + state = ACTIVE_R; + break; + case ACTIVE_W: + assert(poller); + poller->modFd(*this, Poller::INOUT); + state = ACTIVE_RW; + break; + } +} + +void DispatchHandle::rewatchWrite() { + if (!writableCallback) { + return; + } + + ScopedLock<Mutex> lock(stateLock); + switch(state) { + case IDLE: + case DELAYED_IDLE: + break; + case DELAYED_W: + case DELAYED_RW: + case DELAYED_DELETE: + break; + case DELAYED_R: + state = DELAYED_RW; + break; + case DELAYED_INACTIVE: + state = DELAYED_W; + break; + case INACTIVE: + assert(poller); + poller->modFd(*this, Poller::OUTPUT); + state = ACTIVE_W; + break; + case ACTIVE_R: + assert(poller); + poller->modFd(*this, Poller::INOUT); + state = ACTIVE_RW; + break; + case ACTIVE_W: + case ACTIVE_RW: + // Nothing to do: already waiting for writable + break; + } +} + +void DispatchHandle::unwatchRead() { + if (!readableCallback) { + return; + } + + ScopedLock<Mutex> lock(stateLock); + switch(state) { + case IDLE: + case DELAYED_IDLE: + break; + case DELAYED_R: + state = DELAYED_INACTIVE; + break; + case DELAYED_RW: + state = DELAYED_W; + break; + case DELAYED_W: + case DELAYED_INACTIVE: + case DELAYED_DELETE: + break; + case ACTIVE_R: + assert(poller); + poller->modFd(*this, Poller::NONE); + state = INACTIVE; + break; + case ACTIVE_RW: + assert(poller); + poller->modFd(*this, Poller::OUTPUT); + state = ACTIVE_W; + break; + case ACTIVE_W: + case INACTIVE: + break; + } +} + +void DispatchHandle::unwatchWrite() { + if (!writableCallback) { + return; + } + + ScopedLock<Mutex> lock(stateLock); + switch(state) { + case IDLE: + case DELAYED_IDLE: + break; + case DELAYED_W: + state = DELAYED_INACTIVE; + break; + case DELAYED_RW: + state = DELAYED_R; + break; + case DELAYED_R: + case DELAYED_INACTIVE: + case DELAYED_DELETE: + break; + case ACTIVE_W: + assert(poller); + poller->modFd(*this, Poller::NONE); + state = INACTIVE; + break; + case ACTIVE_RW: + assert(poller); + poller->modFd(*this, Poller::INPUT); + state = ACTIVE_R; + break; + case ACTIVE_R: + case INACTIVE: + break; + } +} + +void DispatchHandle::unwatch() { + ScopedLock<Mutex> lock(stateLock); + switch (state) { + case IDLE: + case DELAYED_IDLE: + break; + case DELAYED_R: + case DELAYED_W: + case DELAYED_RW: + case DELAYED_INACTIVE: + state = DELAYED_INACTIVE; + break; + case DELAYED_DELETE: + break; + default: + assert(poller); + poller->modFd(*this, Poller::NONE); + state = INACTIVE; + break; + } +} + +void DispatchHandle::stopWatch() { + ScopedLock<Mutex> lock(stateLock); + switch (state) { + case IDLE: + case DELAYED_IDLE: + case DELAYED_DELETE: + return; + case DELAYED_R: + case DELAYED_W: + case DELAYED_RW: + case DELAYED_INACTIVE: + state = DELAYED_IDLE; + break; + default: + state = IDLE; + break; + } + assert(poller); + poller->delFd(*this); + poller.reset(); +} + +// The slightly strange switch structure +// is to ensure that the lock is released before +// we do the delete +void DispatchHandle::doDelete() { + // Ensure that we're no longer watching anything + stopWatch(); + + // If we're in the middle of a callback defer the delete + { + ScopedLock<Mutex> lock(stateLock); + switch (state) { + case DELAYED_IDLE: + case DELAYED_DELETE: + state = DELAYED_DELETE; + return; + case IDLE: + break; + default: + // Can only get out of stopWatch() in DELAYED_IDLE/DELAYED_DELETE/IDLE states + assert(false); + } + } + // If we're not then do it right away + delete this; +} + +void DispatchHandle::processEvent(Poller::EventType type) { + // Note that we are now doing the callbacks + { + ScopedLock<Mutex> lock(stateLock); + + // Set up to wait for same events next time unless reset + switch(state) { + case ACTIVE_R: + state = DELAYED_R; + break; + case ACTIVE_W: + state = DELAYED_W; + break; + case ACTIVE_RW: + state = DELAYED_RW; + break; + // Can only get here in a DELAYED_* state in the rare case + // that we're already here for reading and we get activated for + // writing and we can write (it might be possible the other way + // round too). In this case we're already processing the handle + // in a different thread in this function so return right away + case DELAYED_R: + case DELAYED_W: + case DELAYED_RW: + case DELAYED_INACTIVE: + case DELAYED_IDLE: + case DELAYED_DELETE: + return; + default: + assert(false); + } + } + + // Do callbacks - whilst we are doing the callbacks we are prevented from processing + // the same handle until we re-enable it. To avoid rentering the callbacks for a single + // handle re-enabling in the callbacks is actually deferred until they are complete. + switch (type) { + case Poller::READABLE: + readableCallback(*this); + break; + case Poller::WRITABLE: + writableCallback(*this); + break; + case Poller::READ_WRITABLE: + readableCallback(*this); + writableCallback(*this); + break; + case Poller::DISCONNECTED: + { + ScopedLock<Mutex> lock(stateLock); + state = DELAYED_INACTIVE; + } + if (disconnectedCallback) { + disconnectedCallback(*this); + } + break; + default: + assert(false); + } + + // If any of the callbacks re-enabled reading/writing then actually + // do it now + { + ScopedLock<Mutex> lock(stateLock); + switch (state) { + case DELAYED_R: + poller->modFd(*this, Poller::INPUT); + state = ACTIVE_R; + return; + case DELAYED_W: + poller->modFd(*this, Poller::OUTPUT); + state = ACTIVE_W; + return; + case DELAYED_RW: + poller->modFd(*this, Poller::INOUT); + state = ACTIVE_RW; + return; + case DELAYED_INACTIVE: + state = INACTIVE; + return; + case DELAYED_IDLE: + state = IDLE; + return; + default: + // This should be impossible + assert(false); + return; + case DELAYED_DELETE: + break; + } + } + delete this; +} + +}} diff --git a/RC5/cpp/src/qpid/sys/DispatchHandle.h b/RC5/cpp/src/qpid/sys/DispatchHandle.h new file mode 100644 index 0000000000..219f2c53d6 --- /dev/null +++ b/RC5/cpp/src/qpid/sys/DispatchHandle.h @@ -0,0 +1,146 @@ +#ifndef _sys_DispatchHandle_h +#define _sys_DispatchHandle_h + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "Poller.h" +#include "Mutex.h" + +#include <boost/function.hpp> + + +namespace qpid { +namespace sys { + +class DispatchHandleRef; +/** + * In order to have your own handle (file descriptor on Unix) watched by the poller + * you need to: + * + * - Subclass IOHandle, in the constructor supply an appropriate + * IOHandlerPrivate object for the platform. + * + * - Construct a DispatchHandle passing it your IOHandle and + * callback functions for read, write and disconnect events. + * + * - Ensure the DispatchHandle is not deleted until the poller is no longer using it. + * TODO: astitcher document DispatchHandleRef to simplify this. + * + * When an event occurs on the handle, the poller calls the relevant callback and + * stops watching that handle. Your callback can call rewatch() or related functions + * to re-enable polling. + */ +class DispatchHandle : public PollerHandle { + friend class DispatchHandleRef; +public: + typedef boost::function1<void, DispatchHandle&> Callback; + +private: + Callback readableCallback; + Callback writableCallback; + Callback disconnectedCallback; + Poller::shared_ptr poller; + Mutex stateLock; + enum { + IDLE, INACTIVE, ACTIVE_R, ACTIVE_W, ACTIVE_RW, + DELAYED_IDLE, DELAYED_INACTIVE, DELAYED_R, DELAYED_W, DELAYED_RW, + DELAYED_DELETE + } state; + +public: + /** + * Provide a handle to poll and a set of callbacks. Note + * callbacks can be 0, meaning you are not interested in that + * event. + * + *@param h: the handle to watch. The IOHandle encapsulates a + * platfrom-specific handle to an IO object (e.g. a file descriptor + * on Unix.) + *@param rCb Callback called when the handle is readable. + *@param wCb Callback called when the handle is writable. + *@param dCb Callback called when the handle is disconnected. + */ + DispatchHandle(const IOHandle& h, Callback rCb, Callback wCb, Callback dCb) : + PollerHandle(h), + readableCallback(rCb), + writableCallback(wCb), + disconnectedCallback(dCb), + state(IDLE) + {} + + ~DispatchHandle(); + + /** Add this DispatchHandle to the poller to be watched. */ + void startWatch(Poller::shared_ptr poller); + + /** Resume watchingn for all non-0 callbacks. */ + void rewatch(); + /** Resume watchingn for read only. */ + void rewatchRead(); + + /** Resume watchingn for write only. */ + void rewatchWrite(); + + /** Stop watching temporarily. The DispatchHandle remains + associated with the poller and can be re-activated using + rewatch. */ + void unwatch(); + /** Stop watching for read */ + void unwatchRead(); + /** Stop watching for write */ + void unwatchWrite(); + + /** Stop watching permanently. Disassociates from the poller. */ + void stopWatch(); + +protected: + /** Override to get extra processing done when the DispatchHandle is deleted. */ + void doDelete(); + +private: + void processEvent(Poller::EventType dir); +}; + +class DispatchHandleRef { + DispatchHandle* ref; + +public: + typedef boost::function1<void, DispatchHandle&> Callback; + DispatchHandleRef(const IOHandle& h, Callback rCb, Callback wCb, Callback dCb) : + ref(new DispatchHandle(h, rCb, wCb, dCb)) + {} + + ~DispatchHandleRef() { ref->doDelete(); } + + void startWatch(Poller::shared_ptr poller) { ref->startWatch(poller); } + void rewatch() { ref->rewatch(); } + void rewatchRead() { ref->rewatchRead(); } + void rewatchWrite() { ref->rewatchWrite(); } + void unwatch() { ref->unwatch(); } + void unwatchRead() { ref->unwatchRead(); } + void unwatchWrite() { ref->unwatchWrite(); } + void stopWatch() { ref->stopWatch(); } +}; + +}} + +#endif // _sys_DispatchHandle_h diff --git a/RC5/cpp/src/qpid/sys/Dispatcher.cpp b/RC5/cpp/src/qpid/sys/Dispatcher.cpp new file mode 100644 index 0000000000..8d1d1b79f5 --- /dev/null +++ b/RC5/cpp/src/qpid/sys/Dispatcher.cpp @@ -0,0 +1,59 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "Dispatcher.h" + +#include <assert.h> + +namespace qpid { +namespace sys { + +Dispatcher::Dispatcher(Poller::shared_ptr poller0) : + poller(poller0) { +} + +Dispatcher::~Dispatcher() { +} + +void Dispatcher::run() { + do { + Poller::Event event = poller->wait(); + + // If can read/write then dispatch appropriate callbacks + if (event.handle) { + event.process(); + } else { + // Handle shutdown + switch (event.type) { + case Poller::SHUTDOWN: + goto dispatcher_shutdown; + default: + // This should be impossible + assert(false); + } + } + } while (true); + +dispatcher_shutdown: + ; +} + +}} diff --git a/RC5/cpp/src/qpid/sys/Dispatcher.h b/RC5/cpp/src/qpid/sys/Dispatcher.h new file mode 100644 index 0000000000..f7c9e8d731 --- /dev/null +++ b/RC5/cpp/src/qpid/sys/Dispatcher.h @@ -0,0 +1,43 @@ +#ifndef _sys_Dispatcher_h +#define _sys_Dispatcher_h + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "Poller.h" +#include "Runnable.h" + +namespace qpid { +namespace sys { + +class Dispatcher : public Runnable { + const Poller::shared_ptr poller; + +public: + Dispatcher(Poller::shared_ptr poller); + ~Dispatcher(); + + void run(); +}; + +}} + +#endif // _sys_Dispatcher_h diff --git a/RC5/cpp/src/qpid/sys/ExceptionHolder.h b/RC5/cpp/src/qpid/sys/ExceptionHolder.h new file mode 100644 index 0000000000..fecaa73eea --- /dev/null +++ b/RC5/cpp/src/qpid/sys/ExceptionHolder.h @@ -0,0 +1,75 @@ +#ifndef QPID_EXCEPTIONHOLDER_H +#define QPID_EXCEPTIONHOLDER_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/memory.h" +#include <boost/shared_ptr.hpp> + + +namespace qpid { +namespace sys { + +struct Raisable { + virtual ~Raisable() {}; + virtual void raise() const=0; + virtual std::string what() const=0; +}; + +/** + * Holder for exceptions. Allows the thread that notices an error condition to + * create an exception and store it to be thrown by another thread. + */ +class ExceptionHolder : public Raisable { + public: + ExceptionHolder() {} + // Use default copy & assign. + + /** Take ownership of ex */ + template <class Ex> ExceptionHolder(Ex* ex) { wrap(ex); } + template <class Ex> ExceptionHolder(const boost::shared_ptr<Ex>& ex) { wrap(ex.release()); } + + template <class Ex> ExceptionHolder& operator=(Ex* ex) { wrap(ex); return *this; } + template <class Ex> ExceptionHolder& operator=(boost::shared_ptr<Ex> ex) { wrap(ex.release()); return *this; } + + void raise() const { if (wrapper.get()) wrapper->raise() ; } + std::string what() const { return wrapper.get() ? wrapper->what() : std::string(); } + bool empty() const { return !wrapper.get(); } + operator bool() const { return !empty(); } + void reset() { wrapper.reset(); } + + private: + template <class Ex> struct Wrapper : public Raisable { + Wrapper(Ex* ptr) : exception(ptr) {} + void raise() const { throw *exception; } + std::string what() const { return exception->what(); } + boost::shared_ptr<Ex> exception; + }; + template <class Ex> void wrap(Ex* ex) { wrapper.reset(new Wrapper<Ex>(ex)); } + boost::shared_ptr<Raisable> wrapper; +}; + + +}} // namespace qpid::sys + + +#endif /*!QPID_EXCEPTIONHOLDER_H*/ diff --git a/RC5/cpp/src/qpid/sys/FileSysDir.h b/RC5/cpp/src/qpid/sys/FileSysDir.h new file mode 100755 index 0000000000..ffe7823f0a --- /dev/null +++ b/RC5/cpp/src/qpid/sys/FileSysDir.h @@ -0,0 +1,62 @@ +#ifndef QPID_SYS_FILESYSDIR_H +#define QPID_SYS_FILESYSDIR_H + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include <string> + +namespace qpid { +namespace sys { + +/** + * @class FileSysDir + * + * Represents a filesystem directory accessible from the local host. + * This class simply checks existence of, and creates, a directory. It could + * be added to later to list contents, etc. + */ +class FileSysDir +{ + const std::string dirPath; + + public: + + FileSysDir (std::string path) : dirPath(path) {} + ~FileSysDir () {} + + /** + * Check to see if the directory exists and is a directory. Throws an + * exception if there is an error checking existence or if the path + * exists but is not a directory. + * + * @retval true if the path exists and is a directory. + * @retval false if the path does not exist. + */ + bool exists (void) const; + + void mkdir(void); + + std::string getPath () { return dirPath; } +}; + +}} // namespace qpid::sys + +#endif /*!QPID_SYS_FILESYSDIR_H*/ diff --git a/RC5/cpp/src/qpid/sys/Fork.h b/RC5/cpp/src/qpid/sys/Fork.h new file mode 100644 index 0000000000..4ec061f7bc --- /dev/null +++ b/RC5/cpp/src/qpid/sys/Fork.h @@ -0,0 +1,24 @@ +#ifndef QPID_SYS_FORK_H +#define QPID_SYS_FORK_H + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "posix/Fork.h" + +#endif /*!QPID_SYS_FORK_H*/ diff --git a/RC5/cpp/src/qpid/sys/IOHandle.h b/RC5/cpp/src/qpid/sys/IOHandle.h new file mode 100644 index 0000000000..0bf2abbafa --- /dev/null +++ b/RC5/cpp/src/qpid/sys/IOHandle.h @@ -0,0 +1,59 @@ +#ifndef _sys_IOHandle_h +#define _sys_IOHandle_h + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +namespace qpid { +namespace sys { + +/** + * This is a class intended to abstract the Unix concept of file descriptor + * or the Windows concept of HANDLE + */ +// Windows-related classes +class AsynchAcceptorPrivate; +class AsynchAcceptResult; +namespace windows { + class AsynchIO; +} + +// General classes +class PollerHandle; +class IOHandlePrivate; +class IOHandle { + + friend class AsynchAcceptorPrivate; + friend class AsynchAcceptResult; + friend class windows::AsynchIO; + + friend class PollerHandle; + +protected: + IOHandlePrivate* const impl; + + IOHandle(IOHandlePrivate*); + virtual ~IOHandle(); +}; + +}} + +#endif // _sys_IOHandle_h diff --git a/RC5/cpp/src/qpid/sys/IntegerTypes.h b/RC5/cpp/src/qpid/sys/IntegerTypes.h new file mode 100755 index 0000000000..89635f033e --- /dev/null +++ b/RC5/cpp/src/qpid/sys/IntegerTypes.h @@ -0,0 +1,31 @@ +#ifndef QPID_SYS_INTEGERTYPES_H +#define QPID_SYS_INTEGERTYPES_H + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#if (defined(_WINDOWS) || defined (WIN32)) && defined(_MSC_VER) +#include "qpid/sys/windows/IntegerTypes.h" +#endif +#if !defined _WINDOWS && !defined WIN32 +#include "qpid/sys/posix/IntegerTypes.h" +#endif + +#endif /*!QPID_SYS_INTEGERTYPES_H*/ diff --git a/RC5/cpp/src/qpid/sys/LockFile.h b/RC5/cpp/src/qpid/sys/LockFile.h new file mode 100644 index 0000000000..2ff8c2f6d4 --- /dev/null +++ b/RC5/cpp/src/qpid/sys/LockFile.h @@ -0,0 +1,80 @@ +#ifndef _sys_LockFile_h +#define _sys_LockFile_h + +/* + * + * Copyright (c) 2008 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <boost/noncopyable.hpp> +#include <boost/shared_ptr.hpp> +#include <string> + +#include "IntegerTypes.h" + +namespace qpid { +namespace sys { + +class LockFilePrivate; + +/** + * @class LockFile + * + * LockFile represents a locked file suitable for a coarse-grain system + * lock. For example, the broker uses this to ensure that only one broker + * runs. A common usage idiom is to store the current "owner" process ID + * in the lock file - if the lock file exists, but the stored process ID + * doesn't, the old owner has probably died without cleaning up the lock + * file. + */ +class LockFile : private boost::noncopyable +{ + boost::shared_ptr<LockFilePrivate> impl; + + std::string path; + bool created; + +public: + LockFile(const std::string& path_, bool create); + ~LockFile(); + + /** + * Read the process ID from the lock file. This method assumes that + * if there is a process ID in the file, it was written there by + * writePid(); thus, it's at the start of the file. + * + * Throws an exception if there is an error reading the file. + * + * @returns The stored process ID. No validity check is done on it. + */ + pid_t readPid(void) const; + + /** + * Write the current process's ID to the lock file. It's written at + * the start of the file and will overwrite any other content that + * may be in the file. + * + * Throws an exception if the write fails. + */ + void writePid(void); +}; + +}} /* namespace qpid::sys */ + +#endif /*!_sys_LockFile_h*/ + + + diff --git a/RC5/cpp/src/qpid/sys/LockPtr.h b/RC5/cpp/src/qpid/sys/LockPtr.h new file mode 100644 index 0000000000..738a864317 --- /dev/null +++ b/RC5/cpp/src/qpid/sys/LockPtr.h @@ -0,0 +1,89 @@ +#ifndef QPID_SYS_LOCKPTR_H +#define QPID_SYS_LOCKPTR_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/sys/Mutex.h" +#include <boost/noncopyable.hpp> + +namespace qpid { +namespace sys { + +class Mutex; + +/** + * LockPtr is a smart pointer to T. It is constructed from a volatile + * T* and a Lock (by default a Mutex). It const_casts away the + * volatile qualifier and locks the Lock for the duration of its + * + * Used in conjuntion with the "volatile" keyword to get the compiler + * to help enforce correct concurrent use of mutli-threaded objects. + * See ochttp://www.ddj.com/cpp/184403766 for a detailed discussion. + * + * To summarize the convention: + * - Declare thread-safe member functions as volatile. + * - Declare instances of the class that may be called concurrently as volatile. + * - Use LockPtr to cast away the volatile qualifier while taking a lock. + * + * This means that code calling on a concurrently-used object + * (declared volatile) can only call thread-safe (volatile) member + * functions. Code that needs to use thread-unsafe members must use a + * LockPtr, thereby acquiring the lock and making it safe to do so. + * + * A good type-safe pattern is the internally-locked object: + * - It has it's own private lock member. + * - All public functions are thread safe and declared volatile. + * - Any thread-unsafe, non-volatile functions are private. + * - Only member function implementations use LockPtr to access private functions. + * + * This encapsulates all the locking logic inside the class. + * + * One nice feature of this convention is the common case where you + * need a public, locked version of some function foo() and also a + * private unlocked version to avoid recursive locks. They can be declared as + * volatile and non-volatile overloads of the same function: + * + * // public + * void Thing::foo() volatile { LockPtr<Thing>(this, myLock)->foo(); } + * // private + * void Thing::foo() { ... do stuff ...} + */ + +template <class T, class Lock> class LockPtr : public boost::noncopyable { + public: + LockPtr(volatile T* p, Lock& l) : ptr(const_cast<T*>(p)), lock(l) { lock.lock(); } + LockPtr(volatile T* p, volatile Lock& l) : ptr(const_cast<T*>(p)), lock(const_cast<Lock&>(l)) { lock.lock(); } + ~LockPtr() { lock.unlock(); } + + T& operator*() { return *ptr; } + T* operator->() { return ptr; } + + private: + T* ptr; + Lock& lock; +}; + + +}} // namespace qpid::sys + + +#endif /*!QPID_SYS_LOCKPTR_H*/ diff --git a/RC5/cpp/src/qpid/sys/Monitor.h b/RC5/cpp/src/qpid/sys/Monitor.h new file mode 100644 index 0000000000..2dd405efaf --- /dev/null +++ b/RC5/cpp/src/qpid/sys/Monitor.h @@ -0,0 +1,49 @@ +#ifndef _sys_Monitor_h +#define _sys_Monitor_h + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "Condition.h" + +namespace qpid { +namespace sys { + +/** + * A monitor is a condition variable and a mutex + */ +class Monitor : public Mutex, public Condition { + public: + inline void wait(); + inline bool wait(const AbsTime& absoluteTime); +}; + + +void Monitor::wait() { + Condition::wait(*this); +} + +bool Monitor::wait(const AbsTime& absoluteTime) { + return Condition::wait(*this, absoluteTime); +} + +}} +#endif /*!_sys_Monitor_h*/ diff --git a/RC5/cpp/src/qpid/sys/Mutex.h b/RC5/cpp/src/qpid/sys/Mutex.h new file mode 100644 index 0000000000..00bf392604 --- /dev/null +++ b/RC5/cpp/src/qpid/sys/Mutex.h @@ -0,0 +1,91 @@ +#ifndef _sys_Mutex_h +#define _sys_Mutex_h + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +namespace qpid { +namespace sys { + +/** + * Scoped lock template: calls lock() in ctor, unlock() in dtor. + * L can be any class with lock() and unlock() functions. + */ +template <class L> +class ScopedLock +{ + public: + ScopedLock(L& l) : mutex(l) { l.lock(); } + ~ScopedLock() { mutex.unlock(); } + private: + L& mutex; +}; + +template <class L> +class ScopedUnlock +{ + public: + ScopedUnlock(L& l) : mutex(l) { l.unlock(); } + ~ScopedUnlock() { mutex.lock(); } + private: + L& mutex; +}; + +template <class L> +class ScopedRlock +{ + public: + ScopedRlock(L& l) : mutex(l) { l.rlock(); } + ~ScopedRlock() { mutex.unlock(); } + private: + L& mutex; +}; + +template <class L> +class ScopedWlock +{ + public: + ScopedWlock(L& l) : mutex(l) { l.wlock(); } + ~ScopedWlock() { mutex.unlock(); } + private: + L& mutex; +}; + +template <class L> +class ConditionalScopedLock +{ + public: + ConditionalScopedLock(L& l) : mutex(l) { acquired = l.trylock(); } + ~ConditionalScopedLock() { if (acquired) mutex.unlock(); } + bool lockAcquired() { return acquired; } + private: + L& mutex; + bool acquired; +}; + +}} + +#ifdef USE_APR_PLATFORM +#include "apr/Mutex.h" +#elif defined (_WIN32) +#include "windows/Mutex.h" +#else +#include "posix/Mutex.h" +#endif + +#endif /*!_sys_Mutex_h*/ diff --git a/RC5/cpp/src/qpid/sys/OutputControl.h b/RC5/cpp/src/qpid/sys/OutputControl.h new file mode 100644 index 0000000000..e9e6c57a9b --- /dev/null +++ b/RC5/cpp/src/qpid/sys/OutputControl.h @@ -0,0 +1,42 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "IntegerTypes.h" + +#ifndef _OutputControl_ +#define _OutputControl_ + +namespace qpid { +namespace sys { + + class OutputControl + { + public: + virtual ~OutputControl() {} + virtual void activateOutput() = 0; + virtual void giveReadCredit(int32_t credit) = 0; + }; + +} +} + + +#endif diff --git a/RC5/cpp/src/qpid/sys/OutputTask.h b/RC5/cpp/src/qpid/sys/OutputTask.h new file mode 100644 index 0000000000..005ae7dbc4 --- /dev/null +++ b/RC5/cpp/src/qpid/sys/OutputTask.h @@ -0,0 +1,48 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _OutputTask_ +#define _OutputTask_ + +namespace qpid { +namespace sys { + + class OutputTask + { + public: + virtual ~OutputTask() {} + /** Generate some output. + *@return true if output was generated, false if there is no work to do. + */ + virtual bool doOutput() = 0; + + /** Check if there may be work to do, but don't do it. + * @return True if there may be work to do, false if there is none. + * Can to return a false positive, to allow implementations to do a + * faster check than doOutput(). Must never return a false negative. + */ + virtual bool hasOutput() = 0; + }; + +} +} + + +#endif diff --git a/RC5/cpp/src/qpid/sys/PollableCondition.h b/RC5/cpp/src/qpid/sys/PollableCondition.h new file mode 100644 index 0000000000..56d38f90da --- /dev/null +++ b/RC5/cpp/src/qpid/sys/PollableCondition.h @@ -0,0 +1,28 @@ +#ifndef QPID_SYS_POLLABLECONDITION_H +#define QPID_SYS_POLLABLECONDITION_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +// Currently only has a posix implementation, add #ifdefs for other platforms as needed. +#include "posix/PollableCondition.h" + +#endif /*!QPID_SYS_POLLABLECONDITION_H*/ diff --git a/RC5/cpp/src/qpid/sys/PollableQueue.h b/RC5/cpp/src/qpid/sys/PollableQueue.h new file mode 100644 index 0000000000..7f11cc35a9 --- /dev/null +++ b/RC5/cpp/src/qpid/sys/PollableQueue.h @@ -0,0 +1,152 @@ +#ifndef QPID_SYS_POLLABLEQUEUE_H +#define QPID_SYS_POLLABLEQUEUE_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/sys/PollableCondition.h" +#include "qpid/sys/Dispatcher.h" +#include "qpid/sys/DispatchHandle.h" +#include "qpid/sys/Monitor.h" +#include <boost/function.hpp> +#include <boost/bind.hpp> +#include <algorithm> +#include <deque> + +namespace qpid { +namespace sys { + +class Poller; + +/** + * A queue that can be polled by sys::Poller. Any thread can push to + * the queue, on wakeup the poller thread processes all items on the + * queue by passing them to a callback in a batch. + */ +template <class T> +class PollableQueue { + public: + typedef std::deque<T> Queue; + + /** + * Callback to process a batch of items from the queue. + * @param values to process, any items remaining after call are put back on the queue. + */ + typedef boost::function<void (Queue& values)> Callback; + + /** When the queue is selected by the poller, values are passed to callback cb. */ + PollableQueue(const Callback& cb, const boost::shared_ptr<sys::Poller>& poller); + + ~PollableQueue(); + + /** Push a value onto the queue. Thread safe */ + void push(const T& t); + + /** Start polling. */ + void start(); + + /** Stop polling and wait for the current callback, if any, to complete. */ + void stop(); + + /** Are we currently stopped?*/ + bool isStopped() const { ScopedLock l(lock); return stopped; } + + size_t size() { ScopedLock l(lock); return queue.size(); } + bool empty() { ScopedLock l(lock); return queue.empty(); } + + private: + typedef sys::Monitor::ScopedLock ScopedLock; + typedef sys::Monitor::ScopedUnlock ScopedUnlock; + + void dispatch(sys::DispatchHandle&); + + mutable sys::Monitor lock; + Callback callback; + boost::shared_ptr<sys::Poller> poller; + PollableCondition condition; + DispatchHandle handle; + Queue queue, batch; + Thread dispatcher; + bool stopped; +}; + +template <class T> PollableQueue<T>::PollableQueue( + const Callback& cb, const boost::shared_ptr<sys::Poller>& p) + : callback(cb), poller(p), + handle(condition, boost::bind(&PollableQueue<T>::dispatch, this, _1), 0, 0), stopped(true) +{ + handle.startWatch(poller); + handle.unwatch(); +} + +template <class T> void PollableQueue<T>::start() { + ScopedLock l(lock); + if (!stopped) return; + stopped = false; + if (!queue.empty()) condition.set(); + handle.rewatch(); +} + +template <class T> PollableQueue<T>::~PollableQueue() { + handle.stopWatch(); +} + +template <class T> void PollableQueue<T>::push(const T& t) { + ScopedLock l(lock); + if (queue.empty()) condition.set(); + queue.push_back(t); +} + +template <class T> void PollableQueue<T>::dispatch(sys::DispatchHandle& h) { + ScopedLock l(lock); + assert(dispatcher.id() == 0); + dispatcher = Thread::current(); + while (!stopped && !queue.empty()) { + assert(batch.empty()); + batch.swap(queue); + { + ScopedUnlock u(lock); // Allow concurrent push to queue. + callback(batch); + } + if (!batch.empty()) { + queue.insert(queue.begin(), batch.begin(), batch.end()); // put back unprocessed items. + batch.clear(); + } + } + dispatcher = Thread(); + if (queue.empty()) condition.clear(); + if (stopped) lock.notifyAll(); + else h.rewatch(); +} + +template <class T> void PollableQueue<T>::stop() { + ScopedLock l(lock); + if (stopped) return; + handle.unwatch(); + stopped = true; + // Avoid deadlock if stop is called from the dispatch thread + while (dispatcher.id() && dispatcher.id() != Thread::current().id()) + lock.wait(); +} + +}} // namespace qpid::sys + +#endif /*!QPID_SYS_POLLABLEQUEUE_H*/ diff --git a/RC5/cpp/src/qpid/sys/Poller.h b/RC5/cpp/src/qpid/sys/Poller.h new file mode 100644 index 0000000000..6b7f4d818e --- /dev/null +++ b/RC5/cpp/src/qpid/sys/Poller.h @@ -0,0 +1,109 @@ +#ifndef _sys_Poller_h +#define _sys_Poller_h + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "Time.h" + +#include <boost/shared_ptr.hpp> + +namespace qpid { +namespace sys { + +/** + * Poller: abstract class to encapsulate a file descriptor poll to be used + * by a reactor. + * + * @see DispatchHandler for more details of normal use. + */ +class PollerHandle; +class PollerPrivate; +class Poller { + PollerPrivate* const impl; + +public: + typedef boost::shared_ptr<Poller> shared_ptr; + + enum Direction { + NONE = 0, + INPUT, + OUTPUT, + INOUT + }; + + enum EventType { + INVALID = 0, + READABLE, + WRITABLE, + READ_WRITABLE, + DISCONNECTED, + SHUTDOWN, + TIMEOUT + }; + + struct Event { + PollerHandle* handle; + EventType type; + + Event(PollerHandle* handle0, EventType type0) : + handle(handle0), + type(type0) { + } + + void process(); + }; + + Poller(); + ~Poller(); + /** Note: this function is async-signal safe */ + void shutdown(); + + void addFd(PollerHandle& handle, Direction dir); + void delFd(PollerHandle& handle); + void modFd(PollerHandle& handle, Direction dir); + void rearmFd(PollerHandle& handle); + Event wait(Duration timeout = TIME_INFINITE); +}; + +/** + * Handle class to use for polling + */ +class IOHandle; +class PollerHandlePrivate; +class PollerHandle { + friend class Poller; + friend struct Poller::Event; + + PollerHandlePrivate* const impl; + virtual void processEvent(Poller::EventType) {}; + +public: + PollerHandle(const IOHandle& h); + virtual ~PollerHandle(); +}; + +inline void Poller::Event::process() { + handle->processEvent(type); +} + +}} +#endif // _sys_Poller_h diff --git a/RC5/cpp/src/qpid/sys/ProtocolFactory.h b/RC5/cpp/src/qpid/sys/ProtocolFactory.h new file mode 100644 index 0000000000..56ab404d82 --- /dev/null +++ b/RC5/cpp/src/qpid/sys/ProtocolFactory.h @@ -0,0 +1,58 @@ +#ifndef _sys_ProtocolFactory_h +#define _sys_ProtocolFactory_h + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/sys/IntegerTypes.h" +#include "qpid/SharedObject.h" +#include "ConnectionCodec.h" +#include <boost/function.hpp> + +namespace qpid { +namespace sys { + +class Poller; + +class ProtocolFactory : public qpid::SharedObject<ProtocolFactory> +{ + public: + typedef boost::function2<void, int, std::string> ConnectFailedCallback; + + virtual ~ProtocolFactory() = 0; + virtual uint16_t getPort() const = 0; + virtual std::string getHost() const = 0; + virtual void accept(boost::shared_ptr<Poller>, ConnectionCodec::Factory*) = 0; + virtual void connect( + boost::shared_ptr<Poller>, + const std::string& host, int16_t port, + ConnectionCodec::Factory* codec, + ConnectFailedCallback failed) = 0; + virtual bool supports(const std::string& /*capability*/) { return false; } +}; + +inline ProtocolFactory::~ProtocolFactory() {} + +}} + + + +#endif //!_sys_ProtocolFactory_h diff --git a/RC5/cpp/src/qpid/sys/RdmaIOPlugin.cpp b/RC5/cpp/src/qpid/sys/RdmaIOPlugin.cpp new file mode 100644 index 0000000000..b24dfcc35f --- /dev/null +++ b/RC5/cpp/src/qpid/sys/RdmaIOPlugin.cpp @@ -0,0 +1,356 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "ProtocolFactory.h" + +#include "qpid/Plugin.h" +#include "qpid/broker/Broker.h" +#include "qpid/framing/AMQP_HighestVersion.h" +#include "qpid/log/Statement.h" +#include "qpid/sys/rdma/RdmaIO.h" +#include "qpid/sys/OutputControl.h" + +#include <boost/bind.hpp> +#include <memory> + +#include <netdb.h> + +using std::auto_ptr; +using std::string; +using std::stringstream; + +namespace qpid { +namespace sys { + +class RdmaIOHandler : public OutputControl { + Rdma::Connection::intrusive_ptr connection; + std::string identifier; + Rdma::AsynchIO* aio; + ConnectionCodec::Factory* factory; + ConnectionCodec* codec; + bool readError; + + void write(const framing::ProtocolInitiation&); + + public: + RdmaIOHandler(Rdma::Connection::intrusive_ptr& c, ConnectionCodec::Factory* f); + ~RdmaIOHandler(); + void init(Rdma::AsynchIO* a); + void start(Poller::shared_ptr poller) {aio->start(poller);} + + // Output side + void close(); + void activateOutput(); + void giveReadCredit(int32_t credit); + void initProtocolOut(); + + // Input side + void readbuff(Rdma::AsynchIO& aio, Rdma::Buffer* buff); + void initProtocolIn(Rdma::Buffer* buff); + + // Notifications + void full(Rdma::AsynchIO& aio); + void idle(Rdma::AsynchIO& aio); + void error(Rdma::AsynchIO& aio); +}; + +RdmaIOHandler::RdmaIOHandler(Rdma::Connection::intrusive_ptr& c, qpid::sys::ConnectionCodec::Factory* f) : + connection(c), + identifier(c->getPeerName()), + factory(f), + codec(0), + readError(false) +{ +} + +void RdmaIOHandler::init(Rdma::AsynchIO* a) { + aio = a; +} + +RdmaIOHandler::~RdmaIOHandler() { + if (codec) + codec->closed(); + delete codec; + + aio->deferDelete(); +} + +void RdmaIOHandler::write(const framing::ProtocolInitiation& data) +{ + QPID_LOG(debug, "Rdma: SENT [" << identifier << "] INIT(" << data << ")"); + Rdma::Buffer* buff = aio->getBuffer(); + framing::Buffer out(buff->bytes, buff->byteCount); + data.encode(out); + buff->dataCount = data.encodedSize(); + aio->queueWrite(buff); +} + +void RdmaIOHandler::close() { + aio->queueWriteClose(); +} + +void RdmaIOHandler::activateOutput() { + aio->notifyPendingWrite(); +} + +void RdmaIOHandler::idle(Rdma::AsynchIO&) { + // TODO: Shouldn't need this test as idle() should only ever be called when + // the connection is writable anyway + if ( !(aio->writable() && aio->bufferAvailable()) ) { + return; + } + if (codec == 0) return; + if (codec->canEncode()) { + Rdma::Buffer* buff = aio->getBuffer(); + size_t encoded=codec->encode(buff->bytes, buff->byteCount); + buff->dataCount = encoded; + aio->queueWrite(buff); + } + if (codec->isClosed()) + aio->queueWriteClose(); +} + +void RdmaIOHandler::initProtocolOut() { + // We mustn't have already started the conversation + // but we must be able to send + assert( codec == 0 ); + assert( aio->writable() && aio->bufferAvailable() ); + codec = factory->create(*this, identifier); + write(framing::ProtocolInitiation(codec->getVersion())); +} + +void RdmaIOHandler::error(Rdma::AsynchIO&) { + close(); +} + +void RdmaIOHandler::full(Rdma::AsynchIO&) { + QPID_LOG(debug, "Rdma: buffer full [" << identifier << "]"); +} + +// TODO: Dummy implementation of read throttling +void RdmaIOHandler::giveReadCredit(int32_t) { +} + +// The logic here is subtly different from TCP as RDMA is message oriented +// so we define that an RDMA message is a frame - in this case there is no putting back +// of any message remainder - there shouldn't be any. And what we read here can't be +// smaller than a frame +void RdmaIOHandler::readbuff(Rdma::AsynchIO&, Rdma::Buffer* buff) { + if (readError) { + return; + } + size_t decoded = 0; + try { + if (codec) { + decoded = codec->decode(buff->bytes+buff->dataStart, buff->dataCount); + }else{ + // Need to start protocol processing + initProtocolIn(buff); + } + }catch(const std::exception& e){ + QPID_LOG(error, e.what()); + readError = true; + aio->queueWriteClose(); + } +} + +void RdmaIOHandler::initProtocolIn(Rdma::Buffer* buff) { + framing::Buffer in(buff->bytes+buff->dataStart, buff->dataCount); + framing::ProtocolInitiation protocolInit; + size_t decoded = 0; + if (protocolInit.decode(in)) { + decoded = in.getPosition(); + QPID_LOG(debug, "Rdma: RECV [" << identifier << "] INIT(" << protocolInit << ")"); + + codec = factory->create(protocolInit.getVersion(), *this, identifier); + + // If we failed to create the codec then we don't understand the offered protocol version + if (!codec) { + // send valid version header & close connection. + write(framing::ProtocolInitiation(framing::highestProtocolVersion)); + readError = true; + aio->queueWriteClose(); + } + } +} + +class RdmaIOProtocolFactory : public ProtocolFactory { + auto_ptr<Rdma::Listener> listener; + const uint16_t listeningPort; + + public: + RdmaIOProtocolFactory(int16_t port, int backlog); + void accept(Poller::shared_ptr, ConnectionCodec::Factory*); + void connect(Poller::shared_ptr, const string& host, int16_t port, ConnectionCodec::Factory*, ConnectFailedCallback); + + uint16_t getPort() const; + string getHost() const; + + private: + bool request(Rdma::Connection::intrusive_ptr&, const Rdma::ConnectionParams&, ConnectionCodec::Factory*); + void established(Poller::shared_ptr, Rdma::Connection::intrusive_ptr&); + void connected(Poller::shared_ptr, Rdma::Connection::intrusive_ptr&, const Rdma::ConnectionParams&, ConnectionCodec::Factory*); + void connectionError(Rdma::Connection::intrusive_ptr&, Rdma::ErrorType); + void disconnected(Rdma::Connection::intrusive_ptr&); + void rejected(Rdma::Connection::intrusive_ptr&, const Rdma::ConnectionParams&, ConnectFailedCallback); +}; + +// Static instance to initialise plugin +static class RdmaIOPlugin : public Plugin { + void earlyInitialize(Target&) { + } + + void initialize(Target& target) { + // Check whether we actually have any rdma devices + if ( Rdma::deviceCount() == 0 ) { + QPID_LOG(info, "Rdma: Disabled: no rdma devices found"); + return; + } + + broker::Broker* broker = dynamic_cast<broker::Broker*>(&target); + // Only provide to a Broker + if (broker) { + const broker::Broker::Options& opts = broker->getOptions(); + ProtocolFactory::shared_ptr protocol(new RdmaIOProtocolFactory(opts.port, opts.connectionBacklog)); + QPID_LOG(info, "Rdma: Listening on RDMA port " << protocol->getPort()); + broker->registerProtocolFactory("rdma", protocol); + } + } +} rdmaPlugin; + +RdmaIOProtocolFactory::RdmaIOProtocolFactory(int16_t port, int /*backlog*/) : + listeningPort(port) +{} + +void RdmaIOProtocolFactory::established(Poller::shared_ptr poller, Rdma::Connection::intrusive_ptr& ci) { + RdmaIOHandler* async = ci->getContext<RdmaIOHandler>(); + async->start(poller); +} + +bool RdmaIOProtocolFactory::request(Rdma::Connection::intrusive_ptr& ci, const Rdma::ConnectionParams& cp, + ConnectionCodec::Factory* f) { + try { + RdmaIOHandler* async = new RdmaIOHandler(ci, f); + Rdma::AsynchIO* aio = + new Rdma::AsynchIO(ci->getQueuePair(), + cp.maxRecvBufferSize, cp.initialXmitCredit, Rdma::DEFAULT_WR_ENTRIES, + boost::bind(&RdmaIOHandler::readbuff, async, _1, _2), + boost::bind(&RdmaIOHandler::idle, async, _1), + 0, // boost::bind(&RdmaIOHandler::full, async, _1), + boost::bind(&RdmaIOHandler::error, async, _1)); + async->init(aio); + + // Record aio so we can get it back from a connection + ci->addContext(async); + return true; + } catch (const Rdma::Exception& e) { + QPID_LOG(error, "Rdma: Cannot accept new connection (Rdma exception): " << e.what()); + } catch (const std::exception& e) { + QPID_LOG(error, "Rdma: Cannot accept new connection (unknown exception): " << e.what()); + } + + // If we get here we caught an exception so reject connection + return false; +} + +void RdmaIOProtocolFactory::connectionError(Rdma::Connection::intrusive_ptr&, Rdma::ErrorType) { +} + +void RdmaIOProtocolFactory::disconnected(Rdma::Connection::intrusive_ptr& ci) { + // If we've got a connection already tear it down, otherwise ignore + RdmaIOHandler* async = ci->getContext<RdmaIOHandler>(); + if (async) { + async->close(); + } + delete async; +} + +uint16_t RdmaIOProtocolFactory::getPort() const { + return listeningPort; // Immutable no need for lock. +} + +string RdmaIOProtocolFactory::getHost() const { + //return listener.getSockname(); + return ""; +} + +void RdmaIOProtocolFactory::accept(Poller::shared_ptr poller, ConnectionCodec::Factory* fact) { + ::sockaddr_in sin; + + sin.sin_family = AF_INET; + sin.sin_port = htons(listeningPort); + sin.sin_addr.s_addr = INADDR_ANY; + + listener.reset( + new Rdma::Listener((const sockaddr&)(sin), + Rdma::ConnectionParams(65536, Rdma::DEFAULT_WR_ENTRIES), + boost::bind(&RdmaIOProtocolFactory::established, this, poller, _1), + boost::bind(&RdmaIOProtocolFactory::connectionError, this, _1, _2), + boost::bind(&RdmaIOProtocolFactory::disconnected, this, _1), + boost::bind(&RdmaIOProtocolFactory::request, this, _1, _2, fact))); + + listener->start(poller); +} + +// Only used for outgoing connections (in federation) +void RdmaIOProtocolFactory::rejected(Rdma::Connection::intrusive_ptr&, const Rdma::ConnectionParams&, ConnectFailedCallback failed) { + failed(-1, "Connection rejected"); +} + +// Do the same as connection request and established but mark a client too +void RdmaIOProtocolFactory::connected(Poller::shared_ptr poller, Rdma::Connection::intrusive_ptr& ci, const Rdma::ConnectionParams& cp, + ConnectionCodec::Factory* f) { + (void) request(ci, cp, f); + established(poller, ci); + RdmaIOHandler* async = ci->getContext<RdmaIOHandler>(); + async->initProtocolOut(); +} + +void RdmaIOProtocolFactory::connect( + Poller::shared_ptr poller, + const std::string& host, int16_t p, + ConnectionCodec::Factory* f, + ConnectFailedCallback failed) +{ + ::addrinfo *res; + ::addrinfo hints = {}; + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + stringstream ss; ss << p; + string port = ss.str(); + int n = ::getaddrinfo(host.c_str(), port.c_str(), &hints, &res); + if (n<0) { + throw Exception(QPID_MSG("Rdma: Cannot resolve " << host << ": " << ::gai_strerror(n))); + } + + Rdma::Connector* c = + new Rdma::Connector( + *res->ai_addr, + Rdma::ConnectionParams(8000, Rdma::DEFAULT_WR_ENTRIES), + boost::bind(&RdmaIOProtocolFactory::connected, this, poller, _1, _2, f), + boost::bind(&RdmaIOProtocolFactory::connectionError, this, _1, _2), + boost::bind(&RdmaIOProtocolFactory::disconnected, this, _1), + boost::bind(&RdmaIOProtocolFactory::rejected, this, _1, _2, failed)); + + c->start(poller); +} + +}} // namespace qpid::sys diff --git a/RC5/cpp/src/qpid/sys/Runnable.cpp b/RC5/cpp/src/qpid/sys/Runnable.cpp new file mode 100644 index 0000000000..30122c682f --- /dev/null +++ b/RC5/cpp/src/qpid/sys/Runnable.cpp @@ -0,0 +1,32 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "Runnable.h" +#include <boost/bind.hpp> + +namespace qpid { +namespace sys { + +Runnable::~Runnable() {} + +Runnable::Functor Runnable::functor() +{ + return boost::bind(&Runnable::run, this); +} + +}} diff --git a/RC5/cpp/src/qpid/sys/Runnable.h b/RC5/cpp/src/qpid/sys/Runnable.h new file mode 100644 index 0000000000..fb3927c612 --- /dev/null +++ b/RC5/cpp/src/qpid/sys/Runnable.h @@ -0,0 +1,50 @@ +#ifndef _Runnable_ +#define _Runnable_ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include <boost/function.hpp> + +namespace qpid { +namespace sys { + +/** + * Interface for objects that can be run, e.g. in a thread. + */ +class Runnable +{ + public: + /** Type to represent a runnable as a Functor */ + typedef boost::function0<void> Functor; + + virtual ~Runnable(); + + /** Derived classes override run(). */ + virtual void run() = 0; + + /** Create a functor object that will call this->run(). */ + Functor functor(); +}; + +}} + + +#endif diff --git a/RC5/cpp/src/qpid/sys/ScopedIncrement.h b/RC5/cpp/src/qpid/sys/ScopedIncrement.h new file mode 100644 index 0000000000..8645ab2484 --- /dev/null +++ b/RC5/cpp/src/qpid/sys/ScopedIncrement.h @@ -0,0 +1,67 @@ +#ifndef _posix_ScopedIncrement_h +#define _posix_ScopedIncrement_h + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <boost/noncopyable.hpp> +#include <boost/function.hpp> + +namespace qpid { +namespace sys { + +/** + * Increment counter in constructor and decrement in destructor. + * Optionally call a function if the decremented counter value is 0. + * Note the function must not throw, it is called in the destructor. + */ +template <class T, class F=boost::function<void()> > +class ScopedIncrement : boost::noncopyable +{ + public: + ScopedIncrement(T& c, F f=0) + : count(c), callback(f) { ++count; } + ~ScopedIncrement() { if (--count == 0 && callback) callback(); } + + private: + T& count; + F callback; +}; + + +/** Decrement counter in constructor and increment in destructor. */ +template <class T> +class ScopedDecrement : boost::noncopyable +{ + public: + ScopedDecrement(T& c) : count(c) { value = --count; } + ~ScopedDecrement() { ++count; } + + /** Return the value after the decrement. */ + operator long() { return value; } + + private: + T& count; + long value; +}; + + +}} + + +#endif // _posix_ScopedIncrement_h diff --git a/RC5/cpp/src/qpid/sys/Semaphore.h b/RC5/cpp/src/qpid/sys/Semaphore.h new file mode 100644 index 0000000000..3efb7ce2df --- /dev/null +++ b/RC5/cpp/src/qpid/sys/Semaphore.h @@ -0,0 +1,67 @@ +#ifndef _sys_Semaphore_h +#define _sys_Semaphore_h + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "Monitor.h" + +namespace qpid { +namespace sys { + +class Semaphore +{ +public: + Semaphore(uint c = 1) : count(c) {} + + void lock() { acquire(); } + void unlock() { release(); } + bool trylock() { return tryAcquire(); } + + bool tryAcquire() + { + Monitor::ScopedLock l(monitor); + if (count) { + count--; + return true; + } else { + return false; + } + } + + void acquire() + { + Monitor::ScopedLock l(monitor); + while (count == 0) monitor.wait(); + count--; + } + + void release() + { + Monitor::ScopedLock l(monitor); + if (!count++) monitor.notifyAll(); + } + +private: + Monitor monitor; + uint count; +}; + +}} + +#endif /*!_sys_Semaphore_h*/ diff --git a/RC5/cpp/src/qpid/sys/Shlib.cpp b/RC5/cpp/src/qpid/sys/Shlib.cpp new file mode 100644 index 0000000000..8fd3f42cc6 --- /dev/null +++ b/RC5/cpp/src/qpid/sys/Shlib.cpp @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "Shlib.h" + +#include "qpid/log/Statement.h" + +namespace qpid { +namespace sys { + +AutoShlib::~AutoShlib() throw() { + try { + unload(); + } catch(const std::exception& e) { + QPID_LOG(error, "Unloading shared library: " << e.what()); + } +} + +// Note: Other functions are defined in apr/Shlib.cpp or posix/Shlib.cpp. + +}} // namespace qpid::sys diff --git a/RC5/cpp/src/qpid/sys/Shlib.h b/RC5/cpp/src/qpid/sys/Shlib.h new file mode 100644 index 0000000000..a6d94b42d4 --- /dev/null +++ b/RC5/cpp/src/qpid/sys/Shlib.h @@ -0,0 +1,75 @@ +#ifndef QPID_SYS_SHLIB_H +#define QPID_SYS_SHLIB_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include <boost/noncopyable.hpp> +#include <iostream> + +namespace qpid { +namespace sys { + +/** Encapsulates a shared library handle. + *@see AutoShlib + */ +class Shlib { + public: + /** Load a shared library */ + Shlib(const char* libname) { load(libname); } + + /** Load a shared library */ + Shlib(const std::string& libname) { load(libname.c_str()); } + + /** Unload shared library. */ + void unload(); + + /** Look up symbol. */ + void* getSymbol(const char* symbol); + + /** Look up symbol in shared library, cast it to the desired + * pointer type, void* by default. + */ + template <class T> + T getSymbol(const char* symbol) { + // Double cast avoids warning about casting object to function pointer + return reinterpret_cast<T>(reinterpret_cast<intptr_t>( + this->getSymbol(symbol))); + } + + private: + void* handle; + void load(const char* libname); +}; + +/** A shared library handle that unloads the shlib in it's dtor */ +class AutoShlib : public Shlib { + public: + /** Load shared library */ + AutoShlib(const std::string& libname) : Shlib(libname) {} + /** Calls unload() */ + ~AutoShlib() throw(); +}; + + +}} // namespace qpid::sys + +#endif /*!QPID_SYS_SHLIB_H*/ diff --git a/RC5/cpp/src/qpid/sys/ShutdownHandler.h b/RC5/cpp/src/qpid/sys/ShutdownHandler.h new file mode 100644 index 0000000000..88baecb5b6 --- /dev/null +++ b/RC5/cpp/src/qpid/sys/ShutdownHandler.h @@ -0,0 +1,37 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _ShutdownHandler_ +#define _ShutdownHandler_ + +namespace qpid { +namespace sys { + + class ShutdownHandler + { + public: + virtual void shutdown() = 0; + virtual ~ShutdownHandler(){} + }; + +} +} + +#endif diff --git a/RC5/cpp/src/qpid/sys/Socket.h b/RC5/cpp/src/qpid/sys/Socket.h new file mode 100644 index 0000000000..ae48b8104d --- /dev/null +++ b/RC5/cpp/src/qpid/sys/Socket.h @@ -0,0 +1,110 @@ +#ifndef _sys_Socket_h +#define _sys_Socket_h + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "IOHandle.h" +#include "qpid/sys/IntegerTypes.h" + +#include <string> + +struct sockaddr; + +namespace qpid { +namespace sys { + +class Duration; + +class Socket : public IOHandle +{ +public: + /** Create a socket wrapper for descriptor. */ + Socket(); + + /** Create an initialized TCP socket */ + void createTcp() const; + + /** Set timeout for read and write */ + void setTimeout(const Duration& interval) const; + + /** Set socket non blocking */ + void setNonblocking() const; + + void connect(const std::string& host, uint16_t port) const; + + void close() const; + + /** Bind to a port and start listening. + *@param port 0 means choose an available port. + *@param backlog maximum number of pending connections. + *@return The bound port. + */ + int listen(uint16_t port = 0, int backlog = 10) const; + + /** Returns the "socket name" ie the address bound to + * the near end of the socket + */ + std::string getSockname() const; + + /** Returns the "peer name" ie the address bound to + * the remote end of the socket + */ + std::string getPeername() const; + + /** + * Returns an address (host and port) for the remote end of the + * socket + */ + std::string getPeerAddress() const; + /** + * Returns an address (host and port) for the local end of the + * socket + */ + std::string getLocalAddress() const; + + uint16_t getLocalPort() const; + uint16_t getRemotePort() const; + + /** + * Returns the error code stored in the socket. This may be used + * to determine the result of a non-blocking connect. + */ + int getError() const; + + /** Accept a connection from a socket that is already listening + * and has an incoming connection + */ + Socket* accept(struct sockaddr *addr, socklen_t *addrlen) const; + + // TODO The following are raw operations, maybe they need better wrapping? + int read(void *buf, size_t count) const; + int write(const void *buf, size_t count) const; + + void setTcpNoDelay(bool nodelay) const; + +private: + Socket(IOHandlePrivate*); + mutable std::string connectname; +}; + +}} +#endif /*!_sys_Socket_h*/ diff --git a/RC5/cpp/src/qpid/sys/SslPlugin.cpp b/RC5/cpp/src/qpid/sys/SslPlugin.cpp new file mode 100644 index 0000000000..c519f5dc80 --- /dev/null +++ b/RC5/cpp/src/qpid/sys/SslPlugin.cpp @@ -0,0 +1,184 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "ProtocolFactory.h" + +#include "qpid/Plugin.h" +#include "qpid/sys/ssl/check.h" +#include "qpid/sys/ssl/util.h" +#include "qpid/sys/ssl/SslHandler.h" +#include "qpid/sys/ssl/SslIo.h" +#include "qpid/sys/ssl/SslSocket.h" +#include "qpid/broker/Broker.h" +#include "qpid/log/Statement.h" + +#include <boost/bind.hpp> +#include <memory> + + +namespace qpid { +namespace sys { + +struct SslServerOptions : ssl::SslOptions +{ + uint16_t port; + bool clientAuth; + + SslServerOptions() : port(5671), + clientAuth(false) + { + addOptions() + ("ssl-port", optValue(port, "PORT"), "Port on which to listen for SSL connections") + ("ssl-require-client-authentication", optValue(clientAuth), + "Forces clients to authenticate in order to establish an SSL connection"); + } +}; + +class SslProtocolFactory : public ProtocolFactory { + const bool tcpNoDelay; + qpid::sys::ssl::SslSocket listener; + const uint16_t listeningPort; + std::auto_ptr<qpid::sys::ssl::SslAcceptor> acceptor; + + public: + SslProtocolFactory(const SslServerOptions&, int backlog, bool nodelay); + void accept(Poller::shared_ptr, ConnectionCodec::Factory*); + void connect(Poller::shared_ptr, const std::string& host, int16_t port, + ConnectionCodec::Factory*, + boost::function2<void, int, std::string> failed); + + uint16_t getPort() const; + std::string getHost() const; + bool supports(const std::string& capability); + + private: + void established(Poller::shared_ptr, const qpid::sys::ssl::SslSocket&, ConnectionCodec::Factory*, + bool isClient); +}; + +// Static instance to initialise plugin +static struct SslPlugin : public Plugin { + SslServerOptions options; + + Options* getOptions() { return &options; } + + ~SslPlugin() { ssl::shutdownNSS(); } + + void earlyInitialize(Target&) { + } + + void initialize(Target& target) { + broker::Broker* broker = dynamic_cast<broker::Broker*>(&target); + // Only provide to a Broker + if (broker) { + if (options.certDbPath.empty()) { + QPID_LOG(warning, "SSL plugin not enabled, you must set --qpid-ssl-cert-db to enable it."); + } else { + try { + ssl::initNSS(options, true); + + const broker::Broker::Options& opts = broker->getOptions(); + ProtocolFactory::shared_ptr protocol(new SslProtocolFactory(options, + opts.connectionBacklog, opts.tcpNoDelay)); + QPID_LOG(info, "Listening for SSL connections on TCP port " << protocol->getPort()); + broker->registerProtocolFactory("ssl", protocol); + } catch (const std::exception& e) { + QPID_LOG(error, "Failed to initialise SSL plugin: " << e.what()); + } + } + } + } +} sslPlugin; + +SslProtocolFactory::SslProtocolFactory(const SslServerOptions& options, int backlog, bool nodelay) : + tcpNoDelay(nodelay), listeningPort(listener.listen(options.port, backlog, options.certName, options.clientAuth)) +{} + +void SslProtocolFactory::established(Poller::shared_ptr poller, const qpid::sys::ssl::SslSocket& s, + ConnectionCodec::Factory* f, bool isClient) { + qpid::sys::ssl::SslHandler* async = new qpid::sys::ssl::SslHandler(s.getPeerAddress(), f); + + if (tcpNoDelay) { + s.setTcpNoDelay(tcpNoDelay); + QPID_LOG(info, "Set TCP_NODELAY on connection to " << s.getPeerAddress()); + } + + if (isClient) + async->setClient(); + qpid::sys::ssl::SslIO* aio = new qpid::sys::ssl::SslIO(s, + boost::bind(&qpid::sys::ssl::SslHandler::readbuff, async, _1, _2), + boost::bind(&qpid::sys::ssl::SslHandler::eof, async, _1), + boost::bind(&qpid::sys::ssl::SslHandler::disconnect, async, _1), + boost::bind(&qpid::sys::ssl::SslHandler::closedSocket, async, _1, _2), + boost::bind(&qpid::sys::ssl::SslHandler::nobuffs, async, _1), + boost::bind(&qpid::sys::ssl::SslHandler::idle, async, _1)); + + async->init(aio, 4); + aio->start(poller); +} + +uint16_t SslProtocolFactory::getPort() const { + return listeningPort; // Immutable no need for lock. +} + +std::string SslProtocolFactory::getHost() const { + return listener.getSockname(); +} + +void SslProtocolFactory::accept(Poller::shared_ptr poller, + ConnectionCodec::Factory* fact) { + acceptor.reset( + new qpid::sys::ssl::SslAcceptor(listener, + boost::bind(&SslProtocolFactory::established, this, poller, _1, fact, false))); + acceptor->start(poller); +} + +void SslProtocolFactory::connect( + Poller::shared_ptr poller, + const std::string& host, int16_t port, + ConnectionCodec::Factory* fact, + ConnectFailedCallback failed) +{ + // Note that the following logic does not cause a memory leak. + // The allocated Socket is freed either by the SslConnector + // upon connection failure or by the SslIoHandle upon connection + // shutdown. The allocated SslConnector frees itself when it + // is no longer needed. + + qpid::sys::ssl::SslSocket* socket = new qpid::sys::ssl::SslSocket(); + new qpid::sys::ssl::SslConnector (*socket, poller, host, port, + boost::bind(&SslProtocolFactory::established, this, poller, _1, fact, true), + failed); +} + +namespace +{ +const std::string SSL = "ssl"; +} + +bool SslProtocolFactory::supports(const std::string& capability) +{ + std::string s = capability; + transform(s.begin(), s.end(), s.begin(), tolower); + return s == SSL; +} + +}} // namespace qpid::sys diff --git a/RC5/cpp/src/qpid/sys/StateMonitor.h b/RC5/cpp/src/qpid/sys/StateMonitor.h new file mode 100644 index 0000000000..5a92756f3a --- /dev/null +++ b/RC5/cpp/src/qpid/sys/StateMonitor.h @@ -0,0 +1,78 @@ +#ifndef QPID_SYS_STATEMONITOR_H +#define QPID_SYS_STATEMONITOR_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/sys/Waitable.h" + +#include <bitset> + +namespace qpid { +namespace sys { + +/** + * A monitor with an enum state value. + * + *@param Enum: enum type to use for states. + *@param EnumMax: Highest enum value. + */ +template <class Enum, size_t MaxEnum> +class StateMonitor : public Waitable +{ + public: + struct Set : public std::bitset<MaxEnum + 1> { + Set() {} + Set(Enum s) { set(s); } + Set(Enum s, Enum t) { set(s).set(t); } + Set(Enum s, Enum t, Enum u) { set(s).set(t).set(u); } + Set(Enum s, Enum t, Enum u, Enum v) { set(s).set(t).set(u).set(v); } + }; + + + StateMonitor(Enum initial) { state=initial; } + + /** @pre Caller holds a ScopedLock. */ + void set(Enum s) { state=s; notifyAll(); } + /** @pre Caller holds a ScopedLock. */ + StateMonitor& operator=(Enum s) { set(s); return *this; } + + /** @pre Caller holds a ScopedLock. */ + Enum get() const { return state; } + /** @pre Caller holds a ScopedLock. */ + operator Enum() const { return state; } + + /** @pre Caller holds a ScopedLock */ + void waitFor(Enum s) { ScopedWait(*this); while (s != state) wait(); } + /** @pre Caller holds a ScopedLock */ + void waitFor(Set s) { ScopedWait(*this); while (!s.test(state)) wait(); } + /** @pre Caller holds a ScopedLock */ + void waitNot(Enum s) { ScopedWait(*this); while (s == state) wait(); } + /** @pre Caller holds a ScopedLock */ + void waitNot(Set s) { ScopedWait(*this); while (s.test(state)) wait(); } + + private: + Enum state; +}; + +}} + + +#endif /*!QPID_SYS_STATEMONITOR_H*/ diff --git a/RC5/cpp/src/qpid/sys/StrError.h b/RC5/cpp/src/qpid/sys/StrError.h new file mode 100644 index 0000000000..3843f2abe1 --- /dev/null +++ b/RC5/cpp/src/qpid/sys/StrError.h @@ -0,0 +1,35 @@ +#ifndef _sys_StrError_h +#define _sys_StrError_h + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include <string> + +namespace qpid { +namespace sys { + +/** Get the error message for a system number err, e.g. errno. */ +std::string strError(int err); + +}} // namespace qpid + +#endif // _sys_StrError_h diff --git a/RC5/cpp/src/qpid/sys/SystemInfo.h b/RC5/cpp/src/qpid/sys/SystemInfo.h new file mode 100644 index 0000000000..5a116cf8ee --- /dev/null +++ b/RC5/cpp/src/qpid/sys/SystemInfo.h @@ -0,0 +1,68 @@ +#ifndef QPID_SYS_SYSTEMINFO_H +#define QPID_SYS_SYSTEMINFO_H + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/sys/IntegerTypes.h" +#include "qpid/Address.h" + +namespace qpid { +namespace sys { + +/** + * Retrieve information about the system we are running on. + * Results may be dependent on OS/hardware. + */ +namespace SystemInfo { + /** + * Estimate available concurrency, e.g. number of CPU cores. + * -1 means estimate not available on this platform. + */ + long concurrency(); + + /** + * Get the local host name and set it in the specified TcpAddress. + * Returns false if it can't be obtained and sets errno to any error value. + */ + bool getLocalHostname (TcpAddress &address); + + void getLocalIpAddresses (uint16_t port, std::vector<Address> &addrList); + + /** + * Retrieve system identifiers and versions. This is information that can + * generally be retrieved via POSIX uname(). + * + * @param osName Receives the OS name; e.g., GNU/Linux or Windows + * @param nodeName Receives the nodename. This may or may not match the + * set hostname from getLocalHostname(). + * @param release Receives the OS release identifier. + * @param version Receives the OS release version (kernel, build, sp, etc.) + * @param machine Receives the hardware type. + */ + void getSystemId (std::string &osName, + std::string &nodeName, + std::string &release, + std::string &version, + std::string &machine); + +}}} // namespace qpid::sys::SystemInfo + +#endif /*!QPID_SYS_SYSTEMINFO_H*/ diff --git a/RC5/cpp/src/qpid/sys/TCPIOPlugin.cpp b/RC5/cpp/src/qpid/sys/TCPIOPlugin.cpp new file mode 100644 index 0000000000..a66aca1849 --- /dev/null +++ b/RC5/cpp/src/qpid/sys/TCPIOPlugin.cpp @@ -0,0 +1,146 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "ProtocolFactory.h" +#include "AsynchIOHandler.h" +#include "AsynchIO.h" + +#include "qpid/Plugin.h" +#include "qpid/sys/Socket.h" +#include "qpid/broker/Broker.h" +#include "qpid/log/Statement.h" + +#include <boost/bind.hpp> +#include <memory> + +namespace qpid { +namespace sys { + +class AsynchIOProtocolFactory : public ProtocolFactory { + const bool tcpNoDelay; + Socket listener; + const uint16_t listeningPort; + std::auto_ptr<AsynchAcceptor> acceptor; + + public: + AsynchIOProtocolFactory(int16_t port, int backlog, bool nodelay); + void accept(Poller::shared_ptr, ConnectionCodec::Factory*); + void connect(Poller::shared_ptr, const std::string& host, int16_t port, + ConnectionCodec::Factory*, + boost::function2<void, int, std::string> failed); + + uint16_t getPort() const; + std::string getHost() const; + + private: + void established(Poller::shared_ptr, const Socket&, ConnectionCodec::Factory*, + bool isClient); +}; + +// Static instance to initialise plugin +static class TCPIOPlugin : public Plugin { + void earlyInitialize(Target&) { + } + + void initialize(Target& target) { + broker::Broker* broker = dynamic_cast<broker::Broker*>(&target); + // Only provide to a Broker + if (broker) { + const broker::Broker::Options& opts = broker->getOptions(); + if (opts.requireEncrypted) { + QPID_LOG(info, "Not accepting unencrypted connections on TCP"); + } else { + ProtocolFactory::shared_ptr protocol(new AsynchIOProtocolFactory(opts.port, opts.connectionBacklog, + opts.tcpNoDelay)); + QPID_LOG(info, "Listening on TCP port " << protocol->getPort()); + broker->registerProtocolFactory("tcp", protocol); + } + } + } +} tcpPlugin; + +AsynchIOProtocolFactory::AsynchIOProtocolFactory(int16_t port, int backlog, bool nodelay) : + tcpNoDelay(nodelay), listeningPort(listener.listen(port, backlog)) +{} + +void AsynchIOProtocolFactory::established(Poller::shared_ptr poller, const Socket& s, + ConnectionCodec::Factory* f, bool isClient) { + AsynchIOHandler* async = new AsynchIOHandler(s.getPeerAddress(), f); + + if (tcpNoDelay) { + s.setTcpNoDelay(tcpNoDelay); + QPID_LOG(info, "Set TCP_NODELAY on connection to " << s.getPeerAddress()); + } + + if (isClient) + async->setClient(); + AsynchIO* aio = AsynchIO::create + (s, + boost::bind(&AsynchIOHandler::readbuff, async, _1, _2), + boost::bind(&AsynchIOHandler::eof, async, _1), + boost::bind(&AsynchIOHandler::disconnect, async, _1), + boost::bind(&AsynchIOHandler::closedSocket, async, _1, _2), + boost::bind(&AsynchIOHandler::nobuffs, async, _1), + boost::bind(&AsynchIOHandler::idle, async, _1)); + + async->init(aio, 4); + aio->start(poller); +} + +uint16_t AsynchIOProtocolFactory::getPort() const { + return listeningPort; // Immutable no need for lock. +} + +std::string AsynchIOProtocolFactory::getHost() const { + return listener.getSockname(); +} + +void AsynchIOProtocolFactory::accept(Poller::shared_ptr poller, + ConnectionCodec::Factory* fact) { + acceptor.reset( + new AsynchAcceptor(listener, + boost::bind(&AsynchIOProtocolFactory::established, this, poller, _1, fact, false))); + acceptor->start(poller); +} + +void AsynchIOProtocolFactory::connect( + Poller::shared_ptr poller, + const std::string& host, int16_t port, + ConnectionCodec::Factory* fact, + ConnectFailedCallback failed) +{ + // Note that the following logic does not cause a memory leak. + // The allocated Socket is freed either by the AsynchConnector + // upon connection failure or by the AsynchIO upon connection + // shutdown. The allocated AsynchConnector frees itself when it + // is no longer needed. + + Socket* socket = new Socket(); + AsynchConnector::create (*socket, + poller, + host, + port, + boost::bind(&AsynchIOProtocolFactory::established, + this, poller, _1, fact, true), + failed); +} + +}} // namespace qpid::sys diff --git a/RC5/cpp/src/qpid/sys/Thread.h b/RC5/cpp/src/qpid/sys/Thread.h new file mode 100644 index 0000000000..2fd59621d8 --- /dev/null +++ b/RC5/cpp/src/qpid/sys/Thread.h @@ -0,0 +1,62 @@ +#ifndef _sys_Thread_h +#define _sys_Thread_h + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include <boost/shared_ptr.hpp> + +#ifdef _WIN32 +# define QPID_TSS __declspec(thread) +#elif defined (gcc) +# define QPID_TSS __thread +#else +# define QPID_TSS +#endif + +namespace qpid { +namespace sys { + +class Runnable; +class ThreadPrivate; + +class Thread +{ + boost::shared_ptr<ThreadPrivate> impl; + + public: + Thread(); + explicit Thread(qpid::sys::Runnable*); + explicit Thread(qpid::sys::Runnable&); + + void join(); + + unsigned long id(); + + static Thread current(); + + /** ID of current thread for logging. + * Workaround for broken Thread::current() in APR + */ + static unsigned long logId() { return current().id(); } +}; + +}} +#endif /*!_sys_Thread_h*/ diff --git a/RC5/cpp/src/qpid/sys/Time.h b/RC5/cpp/src/qpid/sys/Time.h new file mode 100644 index 0000000000..d39be95434 --- /dev/null +++ b/RC5/cpp/src/qpid/sys/Time.h @@ -0,0 +1,168 @@ +#ifndef _sys_Time_h +#define _sys_Time_h + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/sys/IntegerTypes.h" +/* + * The platform defines its notion of time as a TimePrivate type. The + * platform's implementation knows how to handle this type. + */ +#if defined (_WIN32) +# include "windows/Time.h" +#else +# include "posix/Time.h" +#endif + +#include <limits> +#include <iosfwd> + +namespace qpid { +namespace sys { + +class Duration; + +/** + * @class AbsTime + * + * Class to represent an instant in time. + * + * The time resolution is in nanosecs, and this is held with 64 bits + * giving a total time span from about 25 million years ago to 25 million + * years hence. As an aside the internal time can sensibly be negative + * meaning before the epoch (probably 1/1/1970 although this class doesn't + * care). + * + * The AbsTime class is a value class and so you don't need to add any + * accessors to its internal state. If you think you want to replace its value, + * you need to construct a new AbsTime and assign it, viz: + * + * AbsTime when = AbsTime::now(); + * ... + * when = AbsTime(when, 2*TIME_SEC); // Advance timer 2 secs + * + * If for some reason you need access to the internal nanosec value you need + * to convert the AbsTime to a Duration and use its conversion to int64_t, viz: + * + * AbsTime now = AbsTime::now(); + * + * int64_t ns = Duration(now); + * + * However note that the nanosecond value that is returned here is not + * defined to be anything in particular and could vary from platform to + * platform. + * + * There are some sensible operations that are currently missing from + * AbsTime, but nearly all that's needed can be done with a mixture of + * AbsTimes and Durations. + * + * For example, convenience operators to add a Duration and AbsTime returning + * an AbsTime would fit here (although you can already perform the operation + * with one of the AbsTime constructors). However trying to add 2 AbsTimes + * doesn't make sense. + */ +class AbsTime { + friend class Duration; + + TimePrivate timepoint; + +public: + inline AbsTime() {} + AbsTime(const AbsTime& time0, const Duration& duration); + // Default assignment operation fine + // Default copy constructor fine + + static AbsTime now(); + static AbsTime FarFuture(); + const TimePrivate& getPrivate(void) const { return timepoint; } + bool operator==(const AbsTime& t) const { return t.timepoint == timepoint; } + template <class S> void serialize(S& s) { s(timepoint); } + + friend bool operator<(const AbsTime& a, const AbsTime& b); + friend bool operator>(const AbsTime& a, const AbsTime& b); + friend std::ostream& operator << (std::ostream&, const AbsTime&); +}; + +std::ostream& operator << (std::ostream&, const AbsTime&); + +/** + * @class Duration + * Class to represent the duration between instants of time. + * + * As AbsTime, this class also uses nanosecs for its time + * resolution where possible. For the most part a duration can be dealt + * with like a 64 bit integer, and indeed there is an implicit conversion which + * makes this quite convenient. + */ +class Duration { + static int64_t max() { return std::numeric_limits<int64_t>::max(); } + int64_t nanosecs; + + friend class AbsTime; + +public: + inline Duration(int64_t time0); + explicit Duration(const AbsTime& time0); + explicit Duration(const AbsTime& start, const AbsTime& finish); + inline operator int64_t() const; +}; + +std::ostream& operator << (std::ostream&, const Duration&); + +inline AbsTime now() { return AbsTime::now(); } + +inline bool operator<(const AbsTime& a, const AbsTime& b) +{ return a.timepoint < b.timepoint; } +inline bool operator>(const AbsTime& a, const AbsTime& b) +{ return a.timepoint > b.timepoint; } + +Duration::Duration(int64_t time0) : + nanosecs(time0) +{} + +Duration::operator int64_t() const +{ return nanosecs; } + +/** Nanoseconds per second. */ +const Duration TIME_SEC = 1000*1000*1000; +/** Nanoseconds per millisecond */ +const Duration TIME_MSEC = 1000*1000; +/** Nanoseconds per microseconds. */ +const Duration TIME_USEC = 1000; +/** Nanoseconds per nanosecond. */ +const Duration TIME_NSEC = 1; + +/** Value to represent an infinite timeout */ +const Duration TIME_INFINITE = std::numeric_limits<int64_t>::max(); + +/** Time greater than any other time */ +const AbsTime FAR_FUTURE = AbsTime::FarFuture(); + +/** Portable sleep for a number of seconds */ +void sleep(int secs); + +/** Portable sleep for a number of microseconds */ +void usleep(uint64_t usecs); + +}} + +#endif /*!_sys_Time_h*/ diff --git a/RC5/cpp/src/qpid/sys/TimeoutHandler.h b/RC5/cpp/src/qpid/sys/TimeoutHandler.h new file mode 100644 index 0000000000..0c10709bbf --- /dev/null +++ b/RC5/cpp/src/qpid/sys/TimeoutHandler.h @@ -0,0 +1,39 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _TimeoutHandler_ +#define _TimeoutHandler_ + +namespace qpid { +namespace sys { + + class TimeoutHandler + { + public: + virtual void idleOut() = 0; + virtual void idleIn() = 0; + virtual ~TimeoutHandler(){} + }; + +} +} + + +#endif diff --git a/RC5/cpp/src/qpid/sys/Waitable.h b/RC5/cpp/src/qpid/sys/Waitable.h new file mode 100644 index 0000000000..7701b6f97d --- /dev/null +++ b/RC5/cpp/src/qpid/sys/Waitable.h @@ -0,0 +1,114 @@ +#ifndef QPID_SYS_WAITABLE_H +#define QPID_SYS_WAITABLE_H + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/sys/Monitor.h" +#include "qpid/sys/ExceptionHolder.h" +#include <assert.h> + +namespace qpid { +namespace sys { + +/** + * A monitor that keeps track of waiting threads. Threads declare a + * ScopedWait around wait() inside a ScopedLock to be considered + * waiters. + * + * Allows waiting threads to be interrupted by an exception. + */ +class Waitable : public Monitor { + public: + Waitable() : waiters(0) {} + + ~Waitable() { assert(waiters == 0); } + + /** Use this inside a scoped lock around the + * call to wait() to be counted as a waiter. + */ + struct ScopedWait { + Waitable& w; + ScopedWait(Waitable& w_) : w(w_) { ++w.waiters; } + ~ScopedWait() { if (--w.waiters==0) w.notifyAll(); } + }; + + /** Block till there are no more waiters in ScopedWaits. + * waitWaiters() does not raise an exception even if waiters + * were interrupted by one. + *@pre Must be called inside a ScopedLock but NOT a ScopedWait. + */ + void waitWaiters() { + while (waiters != 0) + Monitor::wait(); + } + + /** Returns the number of outstanding ScopedWaits. + * Must be called with the lock held. + */ + size_t hasWaiters() const { + return waiters; + } + + /** Set an execption to interrupt waiters in ScopedWait. + * Must be called with the lock held. + */ + void setException(const ExceptionHolder& e) { + exception = e; + notifyAll(); + + } + + /** True if the waitable has an exception */ + bool hasException() const { return exception; } + + /** Clear the exception if any */ + void resetException() { exception.reset(); } + + /** Throws an exception if one is set before or during the wait. */ + void wait() { + ExCheck e(exception); + Monitor::wait(); + } + + /** Throws an exception if one is set before or during the wait. */ + bool wait(const AbsTime& absoluteTime) { + ExCheck e(exception); + return Monitor::wait(absoluteTime); + } + + private: + struct ExCheck { + const ExceptionHolder& exception; + ExCheck(const ExceptionHolder& e) : exception(e) { e.raise(); } + ~ExCheck() { exception.raise(); } + }; + + size_t waiters; + ExceptionHolder exception; + + friend struct ScopedWait; +}; + +}} // namespace qpid::sys + + + +#endif /*!QPID_SYS_WAITABLE_H*/ diff --git a/RC5/cpp/src/qpid/sys/apr/APRBase.cpp b/RC5/cpp/src/qpid/sys/apr/APRBase.cpp new file mode 100644 index 0000000000..724c489303 --- /dev/null +++ b/RC5/cpp/src/qpid/sys/apr/APRBase.cpp @@ -0,0 +1,89 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include <iostream> +#include "qpid/log/Statement.h" +#include "APRBase.h" + +using namespace qpid::sys; + +APRBase* APRBase::instance = 0; + +APRBase* APRBase::getInstance(){ + if(instance == 0){ + instance = new APRBase(); + } + return instance; +} + + +APRBase::APRBase() : count(0){ + apr_initialize(); + CHECK_APR_SUCCESS(apr_pool_create(&pool, 0)); + CHECK_APR_SUCCESS(apr_thread_mutex_create(&mutex, APR_THREAD_MUTEX_NESTED, pool)); +} + +APRBase::~APRBase(){ + CHECK_APR_SUCCESS(apr_thread_mutex_destroy(mutex)); + apr_pool_destroy(pool); + apr_terminate(); +} + +bool APRBase::_increment(){ + bool deleted(false); + CHECK_APR_SUCCESS(apr_thread_mutex_lock(mutex)); + if(this == instance){ + count++; + }else{ + deleted = true; + } + CHECK_APR_SUCCESS(apr_thread_mutex_unlock(mutex)); + return !deleted; +} + +void APRBase::_decrement(){ + APRBase* copy = 0; + CHECK_APR_SUCCESS(apr_thread_mutex_lock(mutex)); + if(--count == 0){ + copy = instance; + instance = 0; + } + CHECK_APR_SUCCESS(apr_thread_mutex_unlock(mutex)); + if(copy != 0){ + delete copy; + } +} + +void APRBase::increment(){ + int count = 0; + while(count++ < 2 && !getInstance()->_increment()) + QPID_LOG(warning, "APR initialization triggered concurrently with termination."); +} + +void APRBase::decrement(){ + getInstance()->_decrement(); +} + +std::string qpid::sys::get_desc(apr_status_t status){ + const int size = 50; + char tmp[size]; + return std::string(apr_strerror(status, tmp, size)); +} + diff --git a/RC5/cpp/src/qpid/sys/apr/APRBase.h b/RC5/cpp/src/qpid/sys/apr/APRBase.h new file mode 100644 index 0000000000..7b5644a129 --- /dev/null +++ b/RC5/cpp/src/qpid/sys/apr/APRBase.h @@ -0,0 +1,74 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _APRBase_ +#define _APRBase_ + +#include <string> +#include <apr_thread_mutex.h> +#include <apr_errno.h> + +namespace qpid { +namespace sys { + + /** + * Use of APR libraries necessitates explicit init and terminate + * calls. Any class using APR libs should obtain the reference to + * this singleton and increment on construction, decrement on + * destruction. This class can then correctly initialise apr + * before the first use and terminate after the last use. + */ + class APRBase{ + static APRBase* instance; + apr_pool_t* pool; + apr_thread_mutex_t* mutex; + int count; + + APRBase(); + ~APRBase(); + static APRBase* getInstance(); + bool _increment(); + void _decrement(); + public: + static void increment(); + static void decrement(); + }; + + //this is also a convenient place for a helper function for error checking: + void check(apr_status_t status, const char* file, const int line); + std::string get_desc(apr_status_t status); + +#define CHECK_APR_SUCCESS(A) qpid::sys::check(A, __FILE__, __LINE__); + +} +} + +// Inlined as it is called *a lot* +void inline qpid::sys::check(apr_status_t status, const char* file, const int line){ + if (status != APR_SUCCESS){ + char tmp[256]; + throw Exception(QPID_MSG(apr_strerror(status, tmp, size))) + } +} + + + + +#endif diff --git a/RC5/cpp/src/qpid/sys/apr/APRPool.cpp b/RC5/cpp/src/qpid/sys/apr/APRPool.cpp new file mode 100644 index 0000000000..e8b71f6e8a --- /dev/null +++ b/RC5/cpp/src/qpid/sys/apr/APRPool.cpp @@ -0,0 +1,41 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "APRPool.h" +#include "APRBase.h" +#include <boost/pool/detail/singleton.hpp> + +using namespace qpid::sys; + +APRPool::APRPool(){ + APRBase::increment(); + CHECK_APR_SUCCESS(apr_pool_create(&pool, NULL)); +} + +APRPool::~APRPool(){ + apr_pool_destroy(pool); + APRBase::decrement(); +} + +apr_pool_t* APRPool::get() { + return boost::details::pool::singleton_default<APRPool>::instance().pool; +} + diff --git a/RC5/cpp/src/qpid/sys/apr/APRPool.h b/RC5/cpp/src/qpid/sys/apr/APRPool.h new file mode 100644 index 0000000000..da7661fcfa --- /dev/null +++ b/RC5/cpp/src/qpid/sys/apr/APRPool.h @@ -0,0 +1,50 @@ +#ifndef _APRPool_ +#define _APRPool_ + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include <boost/noncopyable.hpp> +#include <apr_pools.h> + +namespace qpid { +namespace sys { +/** + * Singleton APR memory pool. + */ +class APRPool : private boost::noncopyable { + public: + APRPool(); + ~APRPool(); + + /** Get singleton instance */ + static apr_pool_t* get(); + + private: + apr_pool_t* pool; +}; + +}} + + + + + +#endif /*!_APRPool_*/ diff --git a/RC5/cpp/src/qpid/sys/apr/Condition.h b/RC5/cpp/src/qpid/sys/apr/Condition.h new file mode 100644 index 0000000000..5e544219ab --- /dev/null +++ b/RC5/cpp/src/qpid/sys/apr/Condition.h @@ -0,0 +1,84 @@ +#ifndef _sys_apr_Condition_h +#define _sys_apr_Condition_h + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "APRPool.h" +#include "qpid/sys/Mutex.h" +#include "qpid/sys/Time.h" + +#include <sys/errno.h> +#include <boost/noncopyable.hpp> +#include <apr_thread_cond.h> + +namespace qpid { +namespace sys { + +/** + * A condition variable for thread synchronization. + */ +class Condition +{ + public: + inline Condition(); + inline ~Condition(); + inline void wait(Mutex&); + inline bool wait(Mutex&, const AbsTime& absoluteTime); + inline void notify(); + inline void notifyAll(); + + private: + apr_thread_cond_t* condition; +}; + + +Condition::Condition() { + CHECK_APR_SUCCESS(apr_thread_cond_create(&condition, APRPool::get())); +} + +Condition::~Condition() { + CHECK_APR_SUCCESS(apr_thread_cond_destroy(condition)); +} + +void Condition::wait(Mutex& mutex) { + CHECK_APR_SUCCESS(apr_thread_cond_wait(condition, mutex.mutex)); +} + +bool Condition::wait(Mutex& mutex, const AbsTime& absoluteTime){ + // APR uses microseconds. + apr_status_t status = + apr_thread_cond_timedwait( + condition, mutex.mutex, Duration(now(), absoluteTime)/TIME_USEC); + if(status != APR_TIMEUP) CHECK_APR_SUCCESS(status); + return status == 0; +} + +void Condition::notify(){ + CHECK_APR_SUCCESS(apr_thread_cond_signal(condition)); +} + +void Condition::notifyAll(){ + CHECK_APR_SUCCESS(apr_thread_cond_broadcast(condition)); +} + +}} +#endif /*!_sys_apr_Condition_h*/ diff --git a/RC5/cpp/src/qpid/sys/apr/Mutex.h b/RC5/cpp/src/qpid/sys/apr/Mutex.h new file mode 100644 index 0000000000..51089c98ff --- /dev/null +++ b/RC5/cpp/src/qpid/sys/apr/Mutex.h @@ -0,0 +1,124 @@ +#ifndef _sys_apr_Mutex_h +#define _sys_apr_Mutex_h + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "APRBase.h" +#include "APRPool.h" + +#include <boost/noncopyable.hpp> +#include <apr_thread_mutex.h> + +namespace qpid { +namespace sys { + +class Condition; + +/** + * Mutex lock. + */ +class Mutex : private boost::noncopyable { + public: + typedef ScopedLock<Mutex> ScopedLock; + typedef ScopedUnlock<Mutex> ScopedUnlock; + + inline Mutex(); + inline ~Mutex(); + inline void lock(); + inline void unlock(); + inline bool trylock(); + + protected: + apr_thread_mutex_t* mutex; + friend class Condition; +}; + +Mutex::Mutex() { + CHECK_APR_SUCCESS(apr_thread_mutex_create(&mutex, APR_THREAD_MUTEX_NESTED, APRPool::get())); +} + +Mutex::~Mutex(){ + CHECK_APR_SUCCESS(apr_thread_mutex_destroy(mutex)); +} + +void Mutex::lock() { + CHECK_APR_SUCCESS(apr_thread_mutex_lock(mutex)); +} +void Mutex::unlock() { + CHECK_APR_SUCCESS(apr_thread_mutex_unlock(mutex)); +} + +bool Mutex::trylock() { + return apr_thread_mutex_trylock(mutex) == 0; +} + + +/** + * RW lock. + */ +class RWlock : private boost::noncopyable { + friend class Condition; + +public: + typedef ScopedRlock<RWlock> ScopedRlock; + typedef ScopedWlock<RWlock> ScopedWlock; + + inline RWlock(); + inline ~RWlock(); + inline void wlock(); // will write-lock + inline void rlock(); // will read-lock + inline void unlock(); + inline bool trywlock(); // will write-try + inline bool tryrlock(); // will read-try + + protected: + apr_thread_mutex_t* mutex; +}; + +RWlock::RWlock() { + CHECK_APR_SUCCESS(apr_thread_mutex_create(&mutex, APR_THREAD_MUTEX_NESTED, APRPool::get())); +} + +RWlock::~RWlock(){ + CHECK_APR_SUCCESS(apr_thread_mutex_destroy(mutex)); +} + +void RWlock::wlock() { + CHECK_APR_SUCCESS(apr_thread_mutex_lock(mutex)); +} + +void RWlock::rlock() { + CHECK_APR_SUCCESS(apr_thread_mutex_lock(mutex)); +} + +void RWlock::unlock() { + CHECK_APR_SUCCESS(apr_thread_mutex_unlock(mutex)); +} + +bool RWlock::trywlock() { + return apr_thread_mutex_trylock(mutex) == 0; +} + +bool RWlock::tryrlock() { + return apr_thread_mutex_trylock(mutex) == 0; +} + + +}} +#endif /*!_sys_apr_Mutex_h*/ diff --git a/RC5/cpp/src/qpid/sys/apr/Shlib.cpp b/RC5/cpp/src/qpid/sys/apr/Shlib.cpp new file mode 100644 index 0000000000..b0ba706713 --- /dev/null +++ b/RC5/cpp/src/qpid/sys/apr/Shlib.cpp @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/sys/Shlib.h" +#include "APRBase.h" +#include "APRPool.h" +#include <apr_dso.h> + +namespace qpid { +namespace sys { + +void Shlib::load(const char* libname) { + apr_dso_handle_t* aprHandle; + CHECK_APR_SUCCESS( + apr_dso_load(&aprHandle, libname, APRPool::get())); + handle=aprHandle; +} + +void Shlib::unload() { + CHECK_APR_SUCCESS( + apr_dso_unload(static_cast<apr_dso_handle_t*>(handle))); +} + +void* Shlib::getSymbol(const char* name) { + apr_dso_handle_sym_t symbol; + CHECK_APR_SUCCESS(apr_dso_sym(&symbol, + static_cast<apr_dso_handle_t*>(handle), + name)); + return (void*) symbol; +} + +}} // namespace qpid::sys diff --git a/RC5/cpp/src/qpid/sys/apr/Socket.cpp b/RC5/cpp/src/qpid/sys/apr/Socket.cpp new file mode 100644 index 0000000000..577268844a --- /dev/null +++ b/RC5/cpp/src/qpid/sys/apr/Socket.cpp @@ -0,0 +1,114 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + + +#include "qpid/sys/Socket.h" + +#include "APRBase.h" +#include "APRPool.h" + +#include <apr_network_io.h> + +namespace qpid { +namespace sys { + +class SocketPrivate { +public: + SocketPrivate(apr_socket_t* s = 0) : + socket(s) + {} + + apr_socket_t* socket; +}; + +Socket::Socket() : + impl(new SocketPrivate) +{ + createTcp(); +} + +Socket::Socket(SocketPrivate* sp) : + impl(sp) +{} + +Socket::~Socket() { + delete impl; +} + +void Socket::createTcp() const { + apr_socket_t*& socket = impl->socket; + apr_socket_t* s; + CHECK_APR_SUCCESS( + apr_socket_create( + &s, APR_INET, SOCK_STREAM, APR_PROTO_TCP, + APRPool::get())); + socket = s; +} + +void Socket::setTimeout(const Duration& interval) const { + apr_socket_t*& socket = impl->socket; + apr_socket_timeout_set(socket, interval/TIME_USEC); +} + +void Socket::connect(const std::string& host, int port) const { + apr_socket_t*& socket = impl->socket; + apr_sockaddr_t* address; + CHECK_APR_SUCCESS( + apr_sockaddr_info_get( + &address, host.c_str(), APR_UNSPEC, port, APR_IPV4_ADDR_OK, + APRPool::get())); + CHECK_APR_SUCCESS(apr_socket_connect(socket, address)); +} + +void Socket::close() const { + apr_socket_t*& socket = impl->socket; + if (socket == 0) return; + CHECK_APR_SUCCESS(apr_socket_close(socket)); + socket = 0; +} + +ssize_t Socket::send(const void* data, size_t size) const +{ + apr_socket_t*& socket = impl->socket; + apr_size_t sent = size; + apr_status_t status = + apr_socket_send(socket, reinterpret_cast<const char*>(data), &sent); + if (APR_STATUS_IS_TIMEUP(status)) return SOCKET_TIMEOUT; + if (APR_STATUS_IS_EOF(status)) return SOCKET_EOF; + CHECK_APR_SUCCESS(status); + return sent; +} + +ssize_t Socket::recv(void* data, size_t size) const +{ + apr_socket_t*& socket = impl->socket; + apr_size_t received = size; + apr_status_t status = + apr_socket_recv(socket, reinterpret_cast<char*>(data), &received); + if (APR_STATUS_IS_TIMEUP(status)) + return SOCKET_TIMEOUT; + if (APR_STATUS_IS_EOF(status)) + return SOCKET_EOF; + CHECK_APR_SUCCESS(status); + return received; +} + +}} // namespace qpid::sys diff --git a/RC5/cpp/src/qpid/sys/apr/Thread.cpp b/RC5/cpp/src/qpid/sys/apr/Thread.cpp new file mode 100644 index 0000000000..3369ef7eb1 --- /dev/null +++ b/RC5/cpp/src/qpid/sys/apr/Thread.cpp @@ -0,0 +1,34 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "Thread.h" +#include "qpid/sys/Runnable.h" + +using namespace qpid::sys; +using qpid::sys::Runnable; + +void* APR_THREAD_FUNC Thread::runRunnable(apr_thread_t* thread, void *data) { + reinterpret_cast<Runnable*>(data)->run(); + CHECK_APR_SUCCESS(apr_thread_exit(thread, APR_SUCCESS)); + return NULL; +} + + diff --git a/RC5/cpp/src/qpid/sys/apr/Thread.h b/RC5/cpp/src/qpid/sys/apr/Thread.h new file mode 100644 index 0000000000..8cbbc0456e --- /dev/null +++ b/RC5/cpp/src/qpid/sys/apr/Thread.h @@ -0,0 +1,106 @@ +#ifndef _sys_apr_Thread_h +#define _sys_apr_Thread_h + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "APRPool.h" +#include "APRBase.h" + +#include <apr_thread_proc.h> +#include <apr_portable.h> + +namespace qpid { +namespace sys { + +class Runnable; + +class Thread +{ + public: + inline static Thread current(); + + /** ID of current thread for logging. + * Workaround for broken Thread::current() in APR + */ + inline static long logId(); + + inline static void yield(); + + inline Thread(); + inline explicit Thread(qpid::sys::Runnable*); + inline explicit Thread(qpid::sys::Runnable&); + + inline void join(); + + inline long id(); + + private: + static void* APR_THREAD_FUNC runRunnable(apr_thread_t* thread, void *data); + inline Thread(apr_thread_t* t); + apr_thread_t* thread; +}; + +Thread::Thread() : thread(0) {} + +Thread::Thread(Runnable* runnable) { + CHECK_APR_SUCCESS( + apr_thread_create(&thread, 0, runRunnable, runnable, APRPool::get())); +} + +Thread::Thread(Runnable& runnable) { + CHECK_APR_SUCCESS( + apr_thread_create(&thread, 0, runRunnable, &runnable, APRPool::get())); +} + +void Thread::join(){ + apr_status_t status; + if (thread != 0) + CHECK_APR_SUCCESS(apr_thread_join(&status, thread)); +} + +long Thread::id() { + return long(thread); +} + +/** ID of current thread for logging. + * Workaround for broken Thread::current() in APR + */ +long Thread::logId() { + return static_cast<long>(apr_os_thread_current()); +} + +Thread::Thread(apr_thread_t* t) : thread(t) {} + +Thread Thread::current(){ + apr_thread_t* thr; + apr_os_thread_t osthr = apr_os_thread_current(); + CHECK_APR_SUCCESS(apr_os_thread_put(&thr, &osthr, APRPool::get())); + return Thread(thr); +} + +void Thread::yield() +{ + apr_thread_yield(); +} + +}} +#endif /*!_sys_apr_Thread_h*/ diff --git a/RC5/cpp/src/qpid/sys/apr/Time.cpp b/RC5/cpp/src/qpid/sys/apr/Time.cpp new file mode 100644 index 0000000000..34e740b144 --- /dev/null +++ b/RC5/cpp/src/qpid/sys/apr/Time.cpp @@ -0,0 +1,36 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/sys/Time.h" + +#include <apr_time.h> + +namespace qpid { +namespace sys { + +AbsTime AbsTime::now() { + AbsTime time_now; + time_now.time_ns = apr_time_now() * TIME_USEC; + return time_now; +} + +}} + diff --git a/RC5/cpp/src/qpid/sys/epoll/EpollPoller.cpp b/RC5/cpp/src/qpid/sys/epoll/EpollPoller.cpp new file mode 100644 index 0000000000..a1e624ea75 --- /dev/null +++ b/RC5/cpp/src/qpid/sys/epoll/EpollPoller.cpp @@ -0,0 +1,371 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/sys/Poller.h" +#include "qpid/sys/IOHandle.h" +#include "qpid/sys/Mutex.h" +#include "qpid/sys/DeletionManager.h" +#include "qpid/sys/posix/check.h" +#include "qpid/sys/posix/PrivatePosix.h" + +#include <sys/epoll.h> +#include <errno.h> + +#include <assert.h> +#include <vector> +#include <exception> + +namespace qpid { +namespace sys { + +// Deletion manager to handle deferring deletion of PollerHandles to when they definitely aren't being used +DeletionManager<PollerHandlePrivate> PollerHandleDeletionManager; + +// Instantiate (and define) class static for DeletionManager +template <> +DeletionManager<PollerHandlePrivate>::AllThreadsStatuses DeletionManager<PollerHandlePrivate>::allThreadsStatuses(0); + +class PollerHandlePrivate { + friend class Poller; + friend class PollerHandle; + + enum FDStat { + ABSENT, + MONITORED, + INACTIVE, + HUNGUP, + MONITORED_HUNGUP, + DELETED + }; + + int fd; + ::__uint32_t events; + PollerHandle* pollerHandle; + FDStat stat; + Mutex lock; + + PollerHandlePrivate(int f, PollerHandle* p) : + fd(f), + events(0), + pollerHandle(p), + stat(ABSENT) { + } + + bool isActive() const { + return stat == MONITORED || stat == MONITORED_HUNGUP; + } + + void setActive() { + stat = (stat == HUNGUP) ? MONITORED_HUNGUP : MONITORED; + } + + bool isInactive() const { + return stat == INACTIVE || stat == HUNGUP; + } + + void setInactive() { + stat = INACTIVE; + } + + bool isIdle() const { + return stat == ABSENT; + } + + void setIdle() { + stat = ABSENT; + } + + bool isHungup() const { + return stat == MONITORED_HUNGUP || stat == HUNGUP; + } + + void setHungup() { + assert(stat == MONITORED); + stat = HUNGUP; + } + + bool isDeleted() const { + return stat == DELETED; + } + + void setDeleted() { + stat = DELETED; + } +}; + +PollerHandle::PollerHandle(const IOHandle& h) : + impl(new PollerHandlePrivate(toFd(h.impl), this)) +{} + +PollerHandle::~PollerHandle() { + { + ScopedLock<Mutex> l(impl->lock); + if (impl->isDeleted()) { + return; + } + if (impl->isActive()) { + impl->setDeleted(); + } + } + PollerHandleDeletionManager.markForDeletion(impl); +} + +/** + * Concrete implementation of Poller to use the Linux specific epoll + * interface + */ +class PollerPrivate { + friend class Poller; + + static const int DefaultFds = 256; + + struct ReadablePipe { + int fds[2]; + + /** + * This encapsulates an always readable pipe which we can add + * to the epoll set to force epoll_wait to return + */ + ReadablePipe() { + QPID_POSIX_CHECK(::pipe(fds)); + // Just write the pipe's fds to the pipe + QPID_POSIX_CHECK(::write(fds[1], fds, 2)); + } + + ~ReadablePipe() { + ::close(fds[0]); + ::close(fds[1]); + } + + int getFD() { + return fds[0]; + } + }; + + static ReadablePipe alwaysReadable; + + const int epollFd; + bool isShutdown; + + static ::__uint32_t directionToEpollEvent(Poller::Direction dir) { + switch (dir) { + case Poller::INPUT: return ::EPOLLIN; + case Poller::OUTPUT: return ::EPOLLOUT; + case Poller::INOUT: return ::EPOLLIN | ::EPOLLOUT; + default: return 0; + } + } + + static Poller::EventType epollToDirection(::__uint32_t events) { + // POLLOUT & POLLHUP are mutually exclusive really, but at least socketpairs + // can give you both! + events = (events & ::EPOLLHUP) ? events & ~::EPOLLOUT : events; + ::__uint32_t e = events & (::EPOLLIN | ::EPOLLOUT); + switch (e) { + case ::EPOLLIN: return Poller::READABLE; + case ::EPOLLOUT: return Poller::WRITABLE; + case ::EPOLLIN | ::EPOLLOUT: return Poller::READ_WRITABLE; + default: + return (events & (::EPOLLHUP | ::EPOLLERR)) ? + Poller::DISCONNECTED : Poller::INVALID; + } + } + + PollerPrivate() : + epollFd(::epoll_create(DefaultFds)), + isShutdown(false) { + QPID_POSIX_CHECK(epollFd); + } + + ~PollerPrivate() { + // It's probably okay to ignore any errors here as there can't be data loss + ::close(epollFd); + } +}; + +PollerPrivate::ReadablePipe PollerPrivate::alwaysReadable; + +void Poller::addFd(PollerHandle& handle, Direction dir) { + PollerHandlePrivate& eh = *handle.impl; + ScopedLock<Mutex> l(eh.lock); + ::epoll_event epe; + int op; + + if (eh.isIdle()) { + op = EPOLL_CTL_ADD; + epe.events = PollerPrivate::directionToEpollEvent(dir) | ::EPOLLONESHOT; + } else { + assert(eh.isActive()); + op = EPOLL_CTL_MOD; + epe.events = eh.events | PollerPrivate::directionToEpollEvent(dir); + } + epe.data.u64 = 0; // Keep valgrind happy + epe.data.ptr = &eh; + + QPID_POSIX_CHECK(::epoll_ctl(impl->epollFd, op, eh.fd, &epe)); + + // Record monitoring state of this fd + eh.events = epe.events; + eh.setActive(); +} + +void Poller::delFd(PollerHandle& handle) { + PollerHandlePrivate& eh = *handle.impl; + ScopedLock<Mutex> l(eh.lock); + assert(!eh.isIdle()); + int rc = ::epoll_ctl(impl->epollFd, EPOLL_CTL_DEL, eh.fd, 0); + // Ignore EBADF since deleting a nonexistent fd has the overall required result! + // And allows the case where a sloppy program closes the fd and then does the delFd() + if (rc == -1 && errno != EBADF) { + QPID_POSIX_CHECK(rc); + } + eh.setIdle(); +} + +// modFd is equivalent to delFd followed by addFd +void Poller::modFd(PollerHandle& handle, Direction dir) { + PollerHandlePrivate& eh = *handle.impl; + ScopedLock<Mutex> l(eh.lock); + assert(!eh.isIdle()); + + ::epoll_event epe; + epe.events = PollerPrivate::directionToEpollEvent(dir) | ::EPOLLONESHOT; + epe.data.u64 = 0; // Keep valgrind happy + epe.data.ptr = &eh; + + QPID_POSIX_CHECK(::epoll_ctl(impl->epollFd, EPOLL_CTL_MOD, eh.fd, &epe)); + + // Record monitoring state of this fd + eh.events = epe.events; + eh.setActive(); +} + +void Poller::rearmFd(PollerHandle& handle) { + PollerHandlePrivate& eh = *handle.impl; + ScopedLock<Mutex> l(eh.lock); + assert(eh.isInactive()); + + ::epoll_event epe; + epe.events = eh.events; + epe.data.u64 = 0; // Keep valgrind happy + epe.data.ptr = &eh; + + QPID_POSIX_CHECK(::epoll_ctl(impl->epollFd, EPOLL_CTL_MOD, eh.fd, &epe)); + + eh.setActive(); +} + +void Poller::shutdown() { + // NB: this function must be async-signal safe, it must not + // call any function that is not async-signal safe. + + // Allow sloppy code to shut us down more than once + if (impl->isShutdown) + return; + + // Don't use any locking here - isshutdown will be visible to all + // after the epoll_ctl() anyway (it's a memory barrier) + impl->isShutdown = true; + + // Add always readable fd to epoll (not EPOLLONESHOT) + int fd = impl->alwaysReadable.getFD(); + ::epoll_event epe; + epe.events = ::EPOLLIN; + epe.data.u64 = 0; // Keep valgrind happy - don't strictly need next line now + epe.data.ptr = 0; + QPID_POSIX_CHECK(::epoll_ctl(impl->epollFd, EPOLL_CTL_ADD, fd, &epe)); +} + +Poller::Event Poller::wait(Duration timeout) { + epoll_event epe; + int timeoutMs = (timeout == TIME_INFINITE) ? -1 : timeout / TIME_MSEC; + + // Repeat until we weren't interupted + do { + PollerHandleDeletionManager.markAllUnusedInThisThread(); + int rc = ::epoll_wait(impl->epollFd, &epe, 1, timeoutMs); + + if (impl->isShutdown) { + PollerHandleDeletionManager.markAllUnusedInThisThread(); + return Event(0, SHUTDOWN); + } + + if (rc ==-1 && errno != EINTR) { + QPID_POSIX_CHECK(rc); + } else if (rc > 0) { + assert(rc == 1); + PollerHandlePrivate& eh = *static_cast<PollerHandlePrivate*>(epe.data.ptr); + + ScopedLock<Mutex> l(eh.lock); + + // the handle could have gone inactive since we left the epoll_wait + if (eh.isActive()) { + PollerHandle* handle = eh.pollerHandle; + + // If the connection has been hungup we could still be readable + // (just not writable), allow us to readable until we get here again + if (epe.events & ::EPOLLHUP) { + if (eh.isHungup()) { + return Event(handle, DISCONNECTED); + } + eh.setHungup(); + } else { + eh.setInactive(); + } + return Event(handle, PollerPrivate::epollToDirection(epe.events)); + } else if (eh.isDeleted()) { + // The handle has been deleted whilst still active and so must be removed + // from the poller + int rc = ::epoll_ctl(impl->epollFd, EPOLL_CTL_DEL, eh.fd, 0); + // Ignore EBADF since it's quite likely that we could race with closing the fd + if (rc == -1 && errno != EBADF) { + QPID_POSIX_CHECK(rc); + } + } + } + // We only get here if one of the following: + // * epoll_wait was interrupted by a signal + // * epoll_wait timed out + // * the state of the handle changed after being returned by epoll_wait + // + // The only things we can do here are return a timeout or wait more. + // Obviously if we timed out we return timeout; if the wait was meant to + // be indefinite then we should never return with a time out so we go again. + // If the wait wasn't indefinite, but we were interrupted then we have to return + // with a timeout as we don't know how long we've waited so far and so we can't + // continue the wait. + if (rc == 0 || timeoutMs != -1) { + PollerHandleDeletionManager.markAllUnusedInThisThread(); + return Event(0, TIMEOUT); + } + } while (true); +} + +// Concrete constructors +Poller::Poller() : + impl(new PollerPrivate()) +{} + +Poller::~Poller() { + delete impl; +} + +}} diff --git a/RC5/cpp/src/qpid/sys/posix/AsynchIO.cpp b/RC5/cpp/src/qpid/sys/posix/AsynchIO.cpp new file mode 100644 index 0000000000..9a5798311b --- /dev/null +++ b/RC5/cpp/src/qpid/sys/posix/AsynchIO.cpp @@ -0,0 +1,595 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/sys/AsynchIO.h" +#include "qpid/sys/Socket.h" +#include "qpid/sys/Time.h" +#include "qpid/log/Statement.h" + +#include "check.h" + +// TODO The basic algorithm here is not really POSIX specific and with a +// bit more abstraction could (should) be promoted to be platform portable +#include <unistd.h> +#include <sys/socket.h> +#include <signal.h> +#include <errno.h> +#include <string.h> + +#include <boost/bind.hpp> + +using namespace qpid::sys; + +namespace { + +struct StaticInit { + StaticInit() { + /** + * Make *process* not generate SIGPIPE when writing to closed + * pipe/socket (necessary as default action is to terminate process) + */ + ::signal(SIGPIPE, SIG_IGN); + }; +} init; + +/* + * We keep per thread state to avoid locking overhead. The assumption is that + * on average all the connections are serviced by all the threads so the state + * recorded in each thread is about the same. If this turns out not to be the + * case we could rebalance the info occasionally. + */ +__thread int threadReadTotal = 0; +__thread int threadMaxRead = 0; +__thread int threadReadCount = 0; +__thread int threadWriteTotal = 0; +__thread int threadWriteCount = 0; +__thread int64_t threadMaxReadTimeNs = 2 * 1000000; // start at 2ms +} + +/* + * Asynch Acceptor + */ +namespace qpid { +namespace sys { + +class AsynchAcceptorPrivate { +public: + AsynchAcceptorPrivate(const Socket& s, AsynchAcceptor::Callback callback); + void start(Poller::shared_ptr poller); + +private: + void readable(DispatchHandle& handle); + +private: + AsynchAcceptor::Callback acceptedCallback; + DispatchHandle handle; + const Socket& socket; + +}; + +}} // namespace qpid::sys + +AsynchAcceptor::AsynchAcceptor(const Socket& s, Callback callback) : + impl(new AsynchAcceptorPrivate(s, callback)) +{} + +AsynchAcceptor::~AsynchAcceptor() +{ delete impl;} + +void AsynchAcceptor::start(Poller::shared_ptr poller) { + impl->start(poller); +} + +AsynchAcceptorPrivate::AsynchAcceptorPrivate(const Socket& s, + AsynchAcceptor::Callback callback) : + acceptedCallback(callback), + handle(s, boost::bind(&AsynchAcceptorPrivate::readable, this, _1), 0, 0), + socket(s) { + + s.setNonblocking(); +} + +void AsynchAcceptorPrivate::start(Poller::shared_ptr poller) { + handle.startWatch(poller); +} + +/* + * We keep on accepting as long as there is something to accept + */ +void AsynchAcceptorPrivate::readable(DispatchHandle& h) { + Socket* s; + do { + errno = 0; + // TODO: Currently we ignore the peers address, perhaps we should + // log it or use it for connection acceptance. + try { + s = socket.accept(0, 0); + if (s) { + acceptedCallback(*s); + } else { + break; + } + } catch (const std::exception& e) { + QPID_LOG(error, "Could not accept socket: " << e.what()); + } + } while (true); + + h.rewatch(); +} + +/* + * Asynch Connector + */ +namespace qpid { +namespace sys { +namespace posix { + +/* + * POSIX version of AsynchIO TCP socket connector. + * + * The class is implemented in terms of DispatchHandle to allow it to be + * deleted by deleting the contained DispatchHandle. + */ +class AsynchConnector : public qpid::sys::AsynchConnector, + private DispatchHandle { + +private: + void connComplete(DispatchHandle& handle); + void failure(int, std::string); + +private: + ConnectedCallback connCallback; + FailedCallback failCallback; + const Socket& socket; + +public: + AsynchConnector(const Socket& socket, + Poller::shared_ptr poller, + std::string hostname, + uint16_t port, + ConnectedCallback connCb, + FailedCallback failCb = 0); +}; + +AsynchConnector::AsynchConnector(const Socket& s, + Poller::shared_ptr poller, + std::string hostname, + uint16_t port, + ConnectedCallback connCb, + FailedCallback failCb) : + DispatchHandle(s, + 0, + boost::bind(&AsynchConnector::connComplete, this, _1), + boost::bind(&AsynchConnector::connComplete, this, _1)), + connCallback(connCb), + failCallback(failCb), + socket(s) +{ + socket.setNonblocking(); + try { + socket.connect(hostname, port); + startWatch(poller); + } catch(std::exception& e) { + failure(-1, std::string(e.what())); + } +} + +void AsynchConnector::connComplete(DispatchHandle& h) +{ + int errCode = socket.getError(); + + h.stopWatch(); + if (errCode == 0) { + connCallback(socket); + DispatchHandle::doDelete(); + } else { + // TODO: This need to be fixed as strerror isn't thread safe + failure(errCode, std::string(::strerror(errCode))); + } +} + +void AsynchConnector::failure(int errCode, std::string message) +{ + if (failCallback) + failCallback(errCode, message); + + socket.close(); + delete &socket; + + DispatchHandle::doDelete(); +} + +} // namespace posix + + +AsynchConnector* qpid::sys::AsynchConnector::create(const Socket& s, + Poller::shared_ptr poller, + std::string hostname, + uint16_t port, + ConnectedCallback connCb, + FailedCallback failCb) +{ + return new qpid::sys::posix::AsynchConnector(s, + poller, + hostname, + port, + connCb, + failCb); +} + +/* + * POSIX version of AsynchIO reader/writer + * + * The class is implemented in terms of DispatchHandle to allow it to be + * deleted by deleting the contained DispatchHandle. + */ +namespace posix { + +class AsynchIO : public qpid::sys::AsynchIO, private DispatchHandle { + +public: + AsynchIO(const Socket& s, + ReadCallback rCb, + EofCallback eofCb, + DisconnectCallback disCb, + ClosedCallback cCb = 0, + BuffersEmptyCallback eCb = 0, + IdleCallback iCb = 0); + + // Methods inherited from qpid::sys::AsynchIO + + virtual void queueForDeletion(); + + virtual void start(Poller::shared_ptr poller); + virtual void queueReadBuffer(BufferBase* buff); + virtual void unread(BufferBase* buff); + virtual void queueWrite(BufferBase* buff); + virtual void notifyPendingWrite(); + virtual void queueWriteClose(); + virtual bool writeQueueEmpty(); + virtual void startReading(); + virtual BufferBase* getQueuedBuffer(); + +private: + ~AsynchIO(); + + // Methods that are callback targets from Dispatcher. + void readable(DispatchHandle& handle); + void writeable(DispatchHandle& handle); + void disconnected(DispatchHandle& handle); + void close(DispatchHandle& handle); + +private: + ReadCallback readCallback; + EofCallback eofCallback; + DisconnectCallback disCallback; + ClosedCallback closedCallback; + BuffersEmptyCallback emptyCallback; + IdleCallback idleCallback; + const Socket& socket; + std::deque<BufferBase*> bufferQueue; + std::deque<BufferBase*> writeQueue; + bool queuedClose; + /** + * This flag is used to detect and handle concurrency between + * calls to notifyPendingWrite() (which can be made from any thread) and + * the execution of the writeable() method (which is always on the + * thread processing this handle. + */ + volatile bool writePending; +}; + +AsynchIO::AsynchIO(const Socket& s, + ReadCallback rCb, EofCallback eofCb, DisconnectCallback disCb, + ClosedCallback cCb, BuffersEmptyCallback eCb, IdleCallback iCb) : + + DispatchHandle(s, + boost::bind(&AsynchIO::readable, this, _1), + boost::bind(&AsynchIO::writeable, this, _1), + boost::bind(&AsynchIO::disconnected, this, _1)), + readCallback(rCb), + eofCallback(eofCb), + disCallback(disCb), + closedCallback(cCb), + emptyCallback(eCb), + idleCallback(iCb), + socket(s), + queuedClose(false), + writePending(false) { + + s.setNonblocking(); +} + +struct deleter +{ + template <typename T> + void operator()(T *ptr){ delete ptr;} +}; + +AsynchIO::~AsynchIO() { + std::for_each( bufferQueue.begin(), bufferQueue.end(), deleter()); + std::for_each( writeQueue.begin(), writeQueue.end(), deleter()); +} + +void AsynchIO::queueForDeletion() { + DispatchHandle::doDelete(); +} + +void AsynchIO::start(Poller::shared_ptr poller) { + DispatchHandle::startWatch(poller); +} + +void AsynchIO::queueReadBuffer(BufferBase* buff) { + assert(buff); + buff->dataStart = 0; + buff->dataCount = 0; + bufferQueue.push_back(buff); + DispatchHandle::rewatchRead(); +} + +void AsynchIO::unread(BufferBase* buff) { + assert(buff); + if (buff->dataStart != 0) { + memmove(buff->bytes, buff->bytes+buff->dataStart, buff->dataCount); + buff->dataStart = 0; + } + bufferQueue.push_front(buff); + DispatchHandle::rewatchRead(); +} + +void AsynchIO::queueWrite(BufferBase* buff) { + assert(buff); + // If we've already closed the socket then throw the write away + if (queuedClose) { + bufferQueue.push_front(buff); + return; + } else { + writeQueue.push_front(buff); + } + writePending = false; + DispatchHandle::rewatchWrite(); +} + +void AsynchIO::notifyPendingWrite() { + writePending = true; + DispatchHandle::rewatchWrite(); +} + +void AsynchIO::queueWriteClose() { + queuedClose = true; + DispatchHandle::rewatchWrite(); +} + +bool AsynchIO::writeQueueEmpty() { + return writeQueue.empty(); +} + +void AsynchIO::startReading() { + DispatchHandle::rewatchRead(); +} + +/** Return a queued buffer if there are enough + * to spare + */ +AsynchIO::BufferBase* AsynchIO::getQueuedBuffer() { + // Always keep at least one buffer (it might have data that was "unread" in it) + if (bufferQueue.size()<=1) + return 0; + BufferBase* buff = bufferQueue.back(); + assert(buff); + buff->dataStart = 0; + buff->dataCount = 0; + bufferQueue.pop_back(); + return buff; +} + +/* + * We keep on reading as long as we have something to read and a buffer to put + * it in + */ +void AsynchIO::readable(DispatchHandle& h) { + int readTotal = 0; + AbsTime readStartTime = AbsTime::now(); + do { + // (Try to) get a buffer + if (!bufferQueue.empty()) { + // Read into buffer + BufferBase* buff = bufferQueue.front(); + assert(buff); + bufferQueue.pop_front(); + errno = 0; + int readCount = buff->byteCount-buff->dataCount; + int rc = socket.read(buff->bytes + buff->dataCount, readCount); + if (rc > 0) { + buff->dataCount += rc; + threadReadTotal += rc; + readTotal += rc; + + if (!readCallback(*this, buff)) { + // We were told to flow control reading at this point + h.unwatchRead(); + break; + } + + if (rc != readCount) { + // If we didn't fill the read buffer then time to stop reading + break; + } + + // Stop reading if we've overrun our timeslot + if (Duration(readStartTime, AbsTime::now()) > threadMaxReadTimeNs) { + break; + } + + } else { + // Put buffer back (at front so it doesn't interfere with unread buffers) + bufferQueue.push_front(buff); + assert(buff); + + // Eof or other side has gone away + if (rc == 0 || errno == ECONNRESET) { + eofCallback(*this); + h.unwatchRead(); + break; + } else if (errno == EAGAIN) { + // We have just put a buffer back so we know + // we can carry on watching for reads + break; + } else { + // Report error then just treat as a socket disconnect + QPID_LOG(error, "Error reading socket: " << qpid::sys::strError(rc) << "(" << rc << ")" ); + eofCallback(*this); + h.unwatchRead(); + break; + } + } + } else { + // Something to read but no buffer + if (emptyCallback) { + emptyCallback(*this); + } + // If we still have no buffers we can't do anything more + if (bufferQueue.empty()) { + h.unwatchRead(); + break; + } + + } + } while (true); + + ++threadReadCount; + threadMaxRead = std::max(threadMaxRead, readTotal); + return; +} + +/* + * We carry on writing whilst we have data to write and we can write + */ +void AsynchIO::writeable(DispatchHandle& h) { + int writeTotal = 0; + do { + // See if we've got something to write + if (!writeQueue.empty()) { + // Write buffer + BufferBase* buff = writeQueue.back(); + writeQueue.pop_back(); + errno = 0; + assert(buff->dataStart+buff->dataCount <= buff->byteCount); + int rc = socket.write(buff->bytes+buff->dataStart, buff->dataCount); + if (rc >= 0) { + threadWriteTotal += rc; + writeTotal += rc; + + // If we didn't write full buffer put rest back + if (rc != buff->dataCount) { + buff->dataStart += rc; + buff->dataCount -= rc; + writeQueue.push_back(buff); + break; + } + + // Recycle the buffer + queueReadBuffer(buff); + + // If we've already written more than the max for reading then stop + // (this is to stop writes dominating reads) + if (writeTotal > threadMaxRead) + break; + } else { + // Put buffer back + writeQueue.push_back(buff); + if (errno == ECONNRESET || errno == EPIPE) { + // Just stop watching for write here - we'll get a + // disconnect callback soon enough + h.unwatchWrite(); + break; + } else if (errno == EAGAIN) { + // We have just put a buffer back so we know + // we can carry on watching for writes + break; + } else { + QPID_POSIX_CHECK(rc); + } + } + } else { + // If we're waiting to close the socket then can do it now as there is nothing to write + if (queuedClose) { + close(h); + break; + } + // Fd is writable, but nothing to write + if (idleCallback) { + writePending = false; + idleCallback(*this); + } + // If we still have no buffers to write we can't do anything more + if (writeQueue.empty() && !writePending && !queuedClose) { + h.unwatchWrite(); + // The following handles the case where writePending is + // set to true after the test above; in this case its + // possible that the unwatchWrite overwrites the + // desired rewatchWrite so we correct that here + if (writePending) + h.rewatchWrite(); + break; + } + } + } while (true); + + ++threadWriteCount; + return; +} + +void AsynchIO::disconnected(DispatchHandle& h) { + // If we've already queued close do it instead of disconnected callback + if (queuedClose) { + close(h); + } else if (disCallback) { + disCallback(*this); + h.unwatch(); + } +} + +/* + * Close the socket and callback to say we've done it + */ +void AsynchIO::close(DispatchHandle& h) { + h.stopWatch(); + socket.close(); + if (closedCallback) { + closedCallback(*this, socket); + } +} + +} // namespace posix + +AsynchIO* qpid::sys::AsynchIO::create(const Socket& s, + AsynchIO::ReadCallback rCb, + AsynchIO::EofCallback eofCb, + AsynchIO::DisconnectCallback disCb, + AsynchIO::ClosedCallback cCb, + AsynchIO::BuffersEmptyCallback eCb, + AsynchIO::IdleCallback iCb) +{ + return new qpid::sys::posix::AsynchIO(s, rCb, eofCb, disCb, cCb, eCb, iCb); +} + +}} // namespace qpid::sys diff --git a/RC5/cpp/src/qpid/sys/posix/Condition.h b/RC5/cpp/src/qpid/sys/posix/Condition.h new file mode 100644 index 0000000000..86d6500ee9 --- /dev/null +++ b/RC5/cpp/src/qpid/sys/posix/Condition.h @@ -0,0 +1,86 @@ +#ifndef _sys_posix_Condition_h +#define _sys_posix_Condition_h + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "PrivatePosix.h" + +#include "qpid/sys/Mutex.h" +#include "qpid/sys/Time.h" + +#include <time.h> +#include <sys/errno.h> +#include <boost/noncopyable.hpp> + +namespace qpid { +namespace sys { + +/** + * A condition variable for thread synchronization. + */ +class Condition +{ + public: + inline Condition(); + inline ~Condition(); + inline void wait(Mutex&); + inline bool wait(Mutex&, const AbsTime& absoluteTime); + inline void notify(); + inline void notifyAll(); + + private: + pthread_cond_t condition; +}; + +Condition::Condition() { + QPID_POSIX_ASSERT_THROW_IF(pthread_cond_init(&condition, 0)); +} + +Condition::~Condition() { + QPID_POSIX_ASSERT_THROW_IF(pthread_cond_destroy(&condition)); +} + +void Condition::wait(Mutex& mutex) { + QPID_POSIX_ASSERT_THROW_IF(pthread_cond_wait(&condition, &mutex.mutex)); +} + +bool Condition::wait(Mutex& mutex, const AbsTime& absoluteTime){ + struct timespec ts; + toTimespec(ts, Duration(absoluteTime)); + int status = pthread_cond_timedwait(&condition, &mutex.mutex, &ts); + if (status != 0) { + if (status == ETIMEDOUT) return false; + throw QPID_POSIX_ERROR(status); + } + return true; +} + +void Condition::notify(){ + QPID_POSIX_ASSERT_THROW_IF(pthread_cond_signal(&condition)); +} + +void Condition::notifyAll(){ + QPID_POSIX_ASSERT_THROW_IF(pthread_cond_broadcast(&condition)); +} + +}} +#endif /*!_sys_posix_Condition_h*/ diff --git a/RC5/cpp/src/qpid/sys/posix/FileSysDir.cpp b/RC5/cpp/src/qpid/sys/posix/FileSysDir.cpp new file mode 100755 index 0000000000..22dc487e74 --- /dev/null +++ b/RC5/cpp/src/qpid/sys/posix/FileSysDir.cpp @@ -0,0 +1,54 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "qpid/sys/FileSysDir.h" +#include "qpid/sys/StrError.h" +#include "qpid/Exception.h" + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <cerrno> +#include <unistd.h> + +namespace qpid { +namespace sys { + +bool FileSysDir::exists (void) const +{ + const char *cpath = dirPath.c_str (); + struct stat s; + if (::stat(cpath, &s)) { + if (errno == ENOENT) { + return false; + } + throw qpid::Exception (strError(errno) + + ": Can't check directory: " + dirPath); + } + if (S_ISDIR(s.st_mode)) + return true; + throw qpid::Exception(dirPath + " is not a directory"); +} + +void FileSysDir::mkdir(void) +{ + if (::mkdir(dirPath.c_str(), 0755)) + throw Exception ("Can't create directory: " + dirPath); +} + +}} // namespace qpid::sys diff --git a/RC5/cpp/src/qpid/sys/posix/Fork.cpp b/RC5/cpp/src/qpid/sys/posix/Fork.cpp new file mode 100644 index 0000000000..ec3af620ef --- /dev/null +++ b/RC5/cpp/src/qpid/sys/posix/Fork.cpp @@ -0,0 +1,127 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +#include "qpid/sys/Fork.h" +#include "qpid/log/Statement.h" +#include "qpid/Exception.h" + +#include <errno.h> +#include <fcntl.h> +#include <signal.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +namespace qpid { +namespace sys { + +using namespace std; + +namespace { + +void writeStr(int fd, const std::string& str) { + const char* WRITE_ERR = "Error writing to parent process"; + int size = str.size(); + if (int(sizeof(size)) > ::write(fd, &size, sizeof(size))) throw ErrnoException(WRITE_ERR); + if (size > ::write(fd, str.data(), size)) throw ErrnoException(WRITE_ERR); +} + +string readStr(int fd) { + string value; + const char* READ_ERR = "Error reading from forked process"; + int size; + if (int(sizeof(size)) > ::read(fd, &size, sizeof(size))) throw ErrnoException(READ_ERR); + if (size > 0) { // Read string message + value.resize(size); + if (size > ::read(fd, const_cast<char*>(value.data()), size)) throw ErrnoException(READ_ERR); + } + return value; +} + +} // namespace + +Fork::Fork() {} +Fork::~Fork() {} + +void Fork::fork() { + pid_t pid = ::fork(); + if (pid < 0) throw ErrnoException("Failed to fork the process"); + if (pid == 0) child(); + else parent(pid); +} + +ForkWithMessage::ForkWithMessage() { + pipeFds[0] = pipeFds[1] = -1; +} + +struct AutoCloseFd { + int fd; + AutoCloseFd(int d) : fd(d) {} + ~AutoCloseFd() { ::close(fd); } +}; + +void ForkWithMessage::fork() { + if(::pipe(pipeFds) < 0) throw ErrnoException("Can't create pipe"); + pid_t pid = ::fork(); + if(pid < 0) throw ErrnoException("Fork fork failed"); + if (pid == 0) { // Child + AutoCloseFd ac(pipeFds[1]); // Write side. + ::close(pipeFds[0]); // Read side + try { + child(); + } + catch (const std::exception& e) { + QPID_LOG(error, "Error in forked child: " << e.what()); + std::string msg = e.what(); + if (msg.empty()) msg = " "; // Make sure we send a non-empty error string. + writeStr(pipeFds[1], msg); + } + } + else { // Parent + close(pipeFds[1]); // Write side. + AutoCloseFd ac(pipeFds[0]); // Read side + parent(pid); + } +} + +string ForkWithMessage::wait(int timeout) { // parent waits for child. + errno = 0; + struct timeval tv; + tv.tv_sec = timeout; + tv.tv_usec = 0; + + fd_set fds; + FD_ZERO(&fds); + FD_SET(pipeFds[0], &fds); + int n=select(FD_SETSIZE, &fds, 0, 0, &tv); + if(n<0) throw ErrnoException("Error waiting for fork"); + if (n==0) throw Exception("Timed out waiting for fork"); + + string error = readStr(pipeFds[0]); + if (error.empty()) return readStr(pipeFds[0]); + else throw Exception("Error in forked process: " + error); +} + +// Write empty error string followed by value string to pipe. +void ForkWithMessage::ready(const string& value) { // child + // Write empty string for error followed by value. + writeStr(pipeFds[1], string()); // No error + writeStr(pipeFds[1], value); +} + + +}} // namespace qpid::sys diff --git a/RC5/cpp/src/qpid/sys/posix/Fork.h b/RC5/cpp/src/qpid/sys/posix/Fork.h new file mode 100644 index 0000000000..698c61ed30 --- /dev/null +++ b/RC5/cpp/src/qpid/sys/posix/Fork.h @@ -0,0 +1,82 @@ +#ifndef QPID_SYS_POSIX_FORK_H +#define QPID_SYS_POSIX_FORK_H + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <string> +#include <sys/types.h> + +namespace qpid { +namespace sys { + +/** + * Fork the process. Call parent() in parent and child() in child. + */ +class Fork { + public: + Fork(); + virtual ~Fork(); + + /** + * Fork the process. + * Calls parent() in the parent process, child() in the child. + */ + virtual void fork(); + + protected: + + /** Called in parent process. + *@child pid of child process + */ + virtual void parent(pid_t child) = 0; + + /** Called in child process */ + virtual void child() = 0; +}; + +/** + * Like Fork but also allows the child to send a string message + * or throw an exception to the parent. + */ +class ForkWithMessage : public Fork { + public: + ForkWithMessage(); + void fork(); + + protected: + /** Call from parent(): wait for child to send a value or throw exception. + * @timeout in seconds to wait for response. + * @return value passed by child to ready(). + */ + std::string wait(int timeout); + + /** Call from child(): Send a value to the parent. + *@param value returned by parent call to wait(). + */ + void ready(const std::string& value); + + private: + int pipeFds[2]; +}; + +}} // namespace qpid::sys + + + +#endif /*!QPID_SYS_POSIX_FORK_H*/ diff --git a/RC5/cpp/src/qpid/sys/posix/IOHandle.cpp b/RC5/cpp/src/qpid/sys/posix/IOHandle.cpp new file mode 100644 index 0000000000..80b487eadc --- /dev/null +++ b/RC5/cpp/src/qpid/sys/posix/IOHandle.cpp @@ -0,0 +1,42 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/sys/IOHandle.h" + +#include "PrivatePosix.h" + +namespace qpid { +namespace sys { + +int toFd(const IOHandlePrivate* h) +{ + return h->fd; +} + +IOHandle::IOHandle(IOHandlePrivate* h) : + impl(h) +{} + +IOHandle::~IOHandle() { + delete impl; +} + +}} // namespace qpid::sys diff --git a/RC5/cpp/src/qpid/sys/posix/IntegerTypes.h b/RC5/cpp/src/qpid/sys/posix/IntegerTypes.h new file mode 100755 index 0000000000..ce97f7bde8 --- /dev/null +++ b/RC5/cpp/src/qpid/sys/posix/IntegerTypes.h @@ -0,0 +1,26 @@ +#ifndef QPID_SYS_POSIX_INTEGERTYPES_H +#define QPID_SYS_POSIX_INTEGERTYPES_H + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include <stdint.h> + +#endif /*!QPID_SYS_INTEGERTYPES_H*/ diff --git a/RC5/cpp/src/qpid/sys/posix/LockFile.cpp b/RC5/cpp/src/qpid/sys/posix/LockFile.cpp new file mode 100755 index 0000000000..af9ecd7d66 --- /dev/null +++ b/RC5/cpp/src/qpid/sys/posix/LockFile.cpp @@ -0,0 +1,89 @@ +/* + * + * Copyright (c) 2008 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "qpid/sys/LockFile.h" + +#include <string> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +#include "check.h" + +namespace qpid { +namespace sys { + +class LockFilePrivate { + friend class LockFile; + + int fd; + +public: + LockFilePrivate(int f) : fd(f) {} +}; + +LockFile::LockFile(const std::string& path_, bool create) + : path(path_), created(create) { + + errno = 0; + int flags=create ? O_WRONLY|O_CREAT|O_NOFOLLOW : O_RDWR; + int fd = ::open(path.c_str(), flags, 0644); + if (fd < 0) throw ErrnoException("Cannot open " + path, errno); + if (::lockf(fd, F_TLOCK, 0) < 0) { + ::close(fd); + throw ErrnoException("Cannot lock " + path, errno); + } + impl.reset(new LockFilePrivate(fd)); +} + +LockFile::~LockFile() { + if (impl) { + int f = impl->fd; + if (f >= 0) { + (void) ::lockf(f, F_ULOCK, 0); // Suppress warnings about ignoring return value. + ::close(f); + impl->fd = -1; + } + } +} + +pid_t LockFile::readPid(void) const { + if (!impl) + throw Exception("Lock file not open"); + + pid_t pid; + int desired_read = sizeof(pid_t); + if (desired_read > ::read(impl->fd, &pid, desired_read) ) { + throw Exception("Cannot read lock file " + path); + } + return pid; +} + +void LockFile::writePid(void) { + if (!impl) + throw Exception("Lock file not open"); + + pid_t pid = getpid(); + int desired_write = sizeof(pid_t); + if (desired_write > ::write(impl->fd, &pid, desired_write)) { + throw Exception("Cannot write lock file " + path); + } +} + +}} /* namespace qpid::sys */ diff --git a/RC5/cpp/src/qpid/sys/posix/Mutex.cpp b/RC5/cpp/src/qpid/sys/posix/Mutex.cpp new file mode 100644 index 0000000000..0e1f0d30c2 --- /dev/null +++ b/RC5/cpp/src/qpid/sys/posix/Mutex.cpp @@ -0,0 +1,46 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/sys/Mutex.h" + +namespace qpid { +namespace sys { + +/** + * Initialise a recursive mutex attr for use in creating mutexes later + * (we use pthread_once to make sure it is initialised exactly once) + */ + +namespace { +pthread_once_t onceControl = PTHREAD_ONCE_INIT; +pthread_mutexattr_t mutexattr; + +void initMutexattr() { + pthread_mutexattr_init(&mutexattr); + pthread_mutexattr_settype(&mutexattr, PTHREAD_MUTEX_RECURSIVE); +} +} + +const pthread_mutexattr_t* Mutex::getAttribute() { + pthread_once(&onceControl, initMutexattr); + return &mutexattr; +} + +}} // namespace qpid::sys diff --git a/RC5/cpp/src/qpid/sys/posix/Mutex.h b/RC5/cpp/src/qpid/sys/posix/Mutex.h new file mode 100644 index 0000000000..cd5a8affd4 --- /dev/null +++ b/RC5/cpp/src/qpid/sys/posix/Mutex.h @@ -0,0 +1,158 @@ +#ifndef _sys_posix_Mutex_h +#define _sys_posix_Mutex_h + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "check.h" + +#include <pthread.h> +#include <boost/noncopyable.hpp> + +namespace qpid { +namespace sys { + +class Condition; + +/** + * Mutex lock. + */ +class Mutex : private boost::noncopyable { + friend class Condition; + static const pthread_mutexattr_t* getAttribute(); + +public: + typedef ::qpid::sys::ScopedLock<Mutex> ScopedLock; + typedef ::qpid::sys::ScopedUnlock<Mutex> ScopedUnlock; + + inline Mutex(); + inline ~Mutex(); + inline void lock(); + inline void unlock(); + inline bool trylock(); + + +protected: + pthread_mutex_t mutex; +}; + +/** + * RW lock. + */ +class RWlock : private boost::noncopyable { + friend class Condition; + +public: + typedef ::qpid::sys::ScopedRlock<RWlock> ScopedRlock; + typedef ::qpid::sys::ScopedWlock<RWlock> ScopedWlock; + + inline RWlock(); + inline ~RWlock(); + inline void wlock(); // will write-lock + inline void rlock(); // will read-lock + inline void unlock(); + inline void trywlock(); // will write-try + inline void tryrlock(); // will read-try + +protected: + pthread_rwlock_t rwlock; +}; + + +/** + * PODMutex is a POD, can be static-initialized with + * PODMutex m = QPID_PODMUTEX_INITIALIZER + */ +struct PODMutex +{ + typedef ::qpid::sys::ScopedLock<PODMutex> ScopedLock; + + inline void lock(); + inline void unlock(); + inline bool trylock(); + + // Must be public to be a POD: + pthread_mutex_t mutex; +}; + +#define QPID_MUTEX_INITIALIZER { PTHREAD_MUTEX_INITIALIZER } + +void PODMutex::lock() { + QPID_POSIX_ASSERT_THROW_IF(pthread_mutex_lock(&mutex)); +} + +void PODMutex::unlock() { + QPID_POSIX_ASSERT_THROW_IF(pthread_mutex_unlock(&mutex)); +} + +bool PODMutex::trylock() { + return pthread_mutex_trylock(&mutex) == 0; +} + +Mutex::Mutex() { + QPID_POSIX_ASSERT_THROW_IF(pthread_mutex_init(&mutex, getAttribute())); +} + +Mutex::~Mutex(){ + QPID_POSIX_ASSERT_THROW_IF(pthread_mutex_destroy(&mutex)); +} + +void Mutex::lock() { + QPID_POSIX_ASSERT_THROW_IF(pthread_mutex_lock(&mutex)); +} + +void Mutex::unlock() { + QPID_POSIX_ASSERT_THROW_IF(pthread_mutex_unlock(&mutex)); +} + +bool Mutex::trylock() { + return pthread_mutex_trylock(&mutex) == 0; +} + + +RWlock::RWlock() { + QPID_POSIX_ASSERT_THROW_IF(pthread_rwlock_init(&rwlock, NULL)); +} + +RWlock::~RWlock(){ + QPID_POSIX_ASSERT_THROW_IF(pthread_rwlock_destroy(&rwlock)); +} + +void RWlock::wlock() { + QPID_POSIX_ASSERT_THROW_IF(pthread_rwlock_wrlock(&rwlock)); +} + +void RWlock::rlock() { + QPID_POSIX_ASSERT_THROW_IF(pthread_rwlock_rdlock(&rwlock)); +} + +void RWlock::unlock() { + QPID_POSIX_ASSERT_THROW_IF(pthread_rwlock_unlock(&rwlock)); +} + +void RWlock::trywlock() { + QPID_POSIX_ASSERT_THROW_IF(pthread_rwlock_trywrlock(&rwlock)); +} + +void RWlock::tryrlock() { + QPID_POSIX_ASSERT_THROW_IF(pthread_rwlock_tryrdlock(&rwlock)); +} + + +}} +#endif /*!_sys_posix_Mutex_h*/ diff --git a/RC5/cpp/src/qpid/sys/posix/PollableCondition.cpp b/RC5/cpp/src/qpid/sys/posix/PollableCondition.cpp new file mode 100644 index 0000000000..0c55fd3c0d --- /dev/null +++ b/RC5/cpp/src/qpid/sys/posix/PollableCondition.cpp @@ -0,0 +1,96 @@ +#ifndef QPID_SYS_LINUX_POLLABLECONDITION_CPP +#define QPID_SYS_LINUX_POLLABLECONDITION_CPP + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "PollableCondition.h" +#include "qpid/sys/posix/PrivatePosix.h" +#include "qpid/Exception.h" + +#include <unistd.h> +#include <fcntl.h> + +namespace qpid { +namespace sys { + +PollableCondition::PollableCondition() : IOHandle(new sys::IOHandlePrivate) { + int fds[2]; + if (::pipe(fds) == -1) + throw ErrnoException(QPID_MSG("Can't create PollableCondition")); + impl->fd = fds[0]; + writeFd = fds[1]; + if (::fcntl(impl->fd, F_SETFL, O_NONBLOCK) == -1) + throw ErrnoException(QPID_MSG("Can't create PollableCondition")); + if (::fcntl(writeFd, F_SETFL, O_NONBLOCK) == -1) + throw ErrnoException(QPID_MSG("Can't create PollableCondition")); +} + +bool PollableCondition::clear() { + char buf[256]; + ssize_t n; + bool wasSet = false; + while ((n = ::read(impl->fd, buf, sizeof(buf))) > 0) + wasSet = true; + if (n == -1 && errno != EAGAIN) throw ErrnoException(QPID_MSG("Error clearing PollableCondition")); + return wasSet; +} + +void PollableCondition::set() { + static const char dummy=0; + ssize_t n = ::write(writeFd, &dummy, 1); + if (n == -1 && errno != EAGAIN) throw ErrnoException("Error setting PollableCondition"); +} + + +#if 0 +// FIXME aconway 2008-08-12: More efficient Linux implementation using +// eventfd system call. Move to separate file & do configure.ac test +// to enable this when ::eventfd() is available. + +#include <sys/eventfd.h> + +namespace qpid { +namespace sys { + +PollableCondition::PollableCondition() : IOHandle(new sys::IOHandlePrivate) { + impl->fd = ::eventfd(0, 0); + if (impl->fd < 0) throw ErrnoException("conditionfd() failed"); +} + +bool PollableCondition::clear() { + char buf[8]; + ssize_t n = ::read(impl->fd, buf, 8); + if (n != 8) throw ErrnoException("read failed on conditionfd"); + return *reinterpret_cast<uint64_t*>(buf); +} + +void PollableCondition::set() { + static const uint64_t value=1; + ssize_t n = ::write(impl->fd, reinterpret_cast<const void*>(&value), 8); + if (n != 8) throw ErrnoException("write failed on conditionfd"); +} + +#endif + +}} // namespace qpid::sys + +#endif /*!QPID_SYS_LINUX_POLLABLECONDITION_CPP*/ diff --git a/RC5/cpp/src/qpid/sys/posix/PollableCondition.h b/RC5/cpp/src/qpid/sys/posix/PollableCondition.h new file mode 100644 index 0000000000..4ec277b0ec --- /dev/null +++ b/RC5/cpp/src/qpid/sys/posix/PollableCondition.h @@ -0,0 +1,56 @@ +#ifndef QPID_SYS_POSIX_POLLABLECONDITION_H +#define QPID_SYS_POSIX_POLLABLECONDITION_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/sys/IOHandle.h" + +namespace qpid { +namespace sys { + +/** + * A pollable condition to integrate in-process conditions with IO + * conditions in a polling loop. + * + * Setting the condition makes it readable for a poller. + * + * Writable/disconnected conditions are undefined and should not be + * polled for. + */ +class PollableCondition : public sys::IOHandle { + public: + PollableCondition(); + + /** Set the condition, triggers readable in a poller. */ + void set(); + + /** Get the current state of the condition, then clear it. + *@return The state of the condition before it was cleared. + */ + bool clear(); + + private: + int writeFd; +}; +}} // namespace qpid::sys + +#endif /*!QPID_SYS_POSIX_POLLABLECONDITION_H*/ diff --git a/RC5/cpp/src/qpid/sys/posix/PrivatePosix.h b/RC5/cpp/src/qpid/sys/posix/PrivatePosix.h new file mode 100644 index 0000000000..33c0cd81bc --- /dev/null +++ b/RC5/cpp/src/qpid/sys/posix/PrivatePosix.h @@ -0,0 +1,52 @@ +#ifndef _sys_posix_PrivatePosix_h +#define _sys_posix_PrivatePosix_h + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/sys/Time.h" + +struct timespec; +struct timeval; + +namespace qpid { +namespace sys { + +// Private Time related implementation details +struct timespec& toTimespec(struct timespec& ts, const Duration& t); +struct timeval& toTimeval(struct timeval& tv, const Duration& t); +Duration toTime(const struct timespec& ts); + +// Private fd related implementation details +class IOHandlePrivate { +public: + IOHandlePrivate(int f = -1) : + fd(f) + {} + + int fd; +}; + +int toFd(const IOHandlePrivate* h); + +}} + +#endif /*!_sys_posix_PrivatePosix_h*/ diff --git a/RC5/cpp/src/qpid/sys/posix/Shlib.cpp b/RC5/cpp/src/qpid/sys/posix/Shlib.cpp new file mode 100644 index 0000000000..62dbfb3dd9 --- /dev/null +++ b/RC5/cpp/src/qpid/sys/posix/Shlib.cpp @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/sys/Shlib.h" +#include "qpid/Exception.h" +#include <dlfcn.h> + + +namespace qpid { +namespace sys { + +void Shlib::load(const char* name) { + dlerror(); + handle = ::dlopen(name, RTLD_NOW); + const char* error = ::dlerror(); + if (error) { + throw Exception(QPID_MSG(error << ": " << name)); + } +} + +void Shlib::unload() { + if (handle) { + ::dlerror(); + ::dlclose(handle); + const char* error = ::dlerror(); + if (error) { + throw Exception(QPID_MSG(error)); + } + handle = 0; + } +} + +void* Shlib::getSymbol(const char* name) { + ::dlerror(); + void* sym = ::dlsym(handle, name); + const char* error = ::dlerror(); + if (error) + throw Exception(QPID_MSG(error << ": " << name)); + return sym; +} + +}} // namespace qpid::sys diff --git a/RC5/cpp/src/qpid/sys/posix/Socket.cpp b/RC5/cpp/src/qpid/sys/posix/Socket.cpp new file mode 100644 index 0000000000..415d5293ef --- /dev/null +++ b/RC5/cpp/src/qpid/sys/posix/Socket.cpp @@ -0,0 +1,264 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/sys/Socket.h" + +#include "check.h" +#include "PrivatePosix.h" + +#include <fcntl.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/errno.h> +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <netdb.h> +#include <cstdlib> +#include <string.h> +#include <iostream> + +#include <boost/format.hpp> + +namespace qpid { +namespace sys { + +namespace { +std::string getName(int fd, bool local, bool includeService = false) +{ + ::sockaddr_storage name; // big enough for any socket address + ::socklen_t namelen = sizeof(name); + + int result = -1; + if (local) { + result = ::getsockname(fd, (::sockaddr*)&name, &namelen); + } else { + result = ::getpeername(fd, (::sockaddr*)&name, &namelen); + } + + QPID_POSIX_CHECK(result); + + char servName[NI_MAXSERV]; + char dispName[NI_MAXHOST]; + if (includeService) { + if (int rc=::getnameinfo((::sockaddr*)&name, namelen, dispName, sizeof(dispName), + servName, sizeof(servName), + NI_NUMERICHOST | NI_NUMERICSERV) != 0) + throw QPID_POSIX_ERROR(rc); + return std::string(dispName) + ":" + std::string(servName); + + } else { + if (int rc=::getnameinfo((::sockaddr*)&name, namelen, dispName, sizeof(dispName), 0, 0, NI_NUMERICHOST) != 0) + throw QPID_POSIX_ERROR(rc); + return dispName; + } +} + +std::string getService(int fd, bool local) +{ + ::sockaddr_storage name; // big enough for any socket address + ::socklen_t namelen = sizeof(name); + + int result = -1; + if (local) { + result = ::getsockname(fd, (::sockaddr*)&name, &namelen); + } else { + result = ::getpeername(fd, (::sockaddr*)&name, &namelen); + } + + QPID_POSIX_CHECK(result); + + char servName[NI_MAXSERV]; + if (int rc=::getnameinfo((::sockaddr*)&name, namelen, 0, 0, + servName, sizeof(servName), + NI_NUMERICHOST | NI_NUMERICSERV) != 0) + throw QPID_POSIX_ERROR(rc); + return servName; +} +} + +Socket::Socket() : + IOHandle(new IOHandlePrivate) +{ + createTcp(); +} + +Socket::Socket(IOHandlePrivate* h) : + IOHandle(h) +{} + +void Socket::createTcp() const +{ + int& socket = impl->fd; + if (socket != -1) Socket::close(); + int s = ::socket (PF_INET, SOCK_STREAM, 0); + if (s < 0) throw QPID_POSIX_ERROR(errno); + socket = s; +} + +void Socket::setTimeout(const Duration& interval) const +{ + const int& socket = impl->fd; + struct timeval tv; + toTimeval(tv, interval); + setsockopt(socket, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)); + setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); +} + +void Socket::setNonblocking() const { + QPID_POSIX_CHECK(::fcntl(impl->fd, F_SETFL, O_NONBLOCK)); +} + +namespace { +const char* h_errstr(int e) { + switch (e) { + case HOST_NOT_FOUND: return "Host not found"; + case NO_ADDRESS: return "Name does not have an IP address"; + case TRY_AGAIN: return "A temporary error occurred on an authoritative name server."; + case NO_RECOVERY: return "Non-recoverable name server error"; + default: return "Unknown error"; + } +} +} + +void Socket::connect(const std::string& host, uint16_t port) const +{ + std::stringstream namestream; + namestream << host << ":" << port; + connectname = namestream.str(); + + const int& socket = impl->fd; + struct sockaddr_in name; + name.sin_family = AF_INET; + name.sin_port = htons(port); + // TODO: Be good to make this work for IPv6 as well as IPv4 + // Use more modern lookup functions + struct hostent* hp = gethostbyname ( host.c_str() ); + if (hp == 0) + throw Exception(QPID_MSG("Cannot resolve " << host << ": " << h_errstr(h_errno))); + ::memcpy(&name.sin_addr.s_addr, hp->h_addr_list[0], hp->h_length); + if ((::connect(socket, (struct sockaddr*)(&name), sizeof(name)) < 0) && + (errno != EINPROGRESS)) + throw qpid::Exception(QPID_MSG(strError(errno) << ": " << host << ":" << port)); +} + +void +Socket::close() const +{ + int& socket = impl->fd; + if (socket == -1) return; + if (::close(socket) < 0) throw QPID_POSIX_ERROR(errno); + socket = -1; +} + +int Socket::listen(uint16_t port, int backlog) const +{ + const int& socket = impl->fd; + int yes=1; + QPID_POSIX_CHECK(setsockopt(socket,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(yes))); + struct sockaddr_in name; + name.sin_family = AF_INET; + name.sin_port = htons(port); + name.sin_addr.s_addr = 0; + if (::bind(socket, (struct sockaddr*)&name, sizeof(name)) < 0) + throw Exception(QPID_MSG("Can't bind to port " << port << ": " << strError(errno))); + if (::listen(socket, backlog) < 0) + throw Exception(QPID_MSG("Can't listen on port " << port << ": " << strError(errno))); + + socklen_t namelen = sizeof(name); + if (::getsockname(socket, (struct sockaddr*)&name, &namelen) < 0) + throw QPID_POSIX_ERROR(errno); + + return ntohs(name.sin_port); +} + +Socket* Socket::accept(struct sockaddr *addr, socklen_t *addrlen) const +{ + int afd = ::accept(impl->fd, addr, addrlen); + if ( afd >= 0) + return new Socket(new IOHandlePrivate(afd)); + else if (errno == EAGAIN) + return 0; + else throw QPID_POSIX_ERROR(errno); +} + +int Socket::read(void *buf, size_t count) const +{ + return ::read(impl->fd, buf, count); +} + +int Socket::write(const void *buf, size_t count) const +{ + return ::write(impl->fd, buf, count); +} + +std::string Socket::getSockname() const +{ + return getName(impl->fd, true); +} + +std::string Socket::getPeername() const +{ + return getName(impl->fd, false); +} + +std::string Socket::getPeerAddress() const +{ + if (!connectname.empty()) + return std::string (connectname); + return getName(impl->fd, false, true); +} + +std::string Socket::getLocalAddress() const +{ + return getName(impl->fd, true, true); +} + +uint16_t Socket::getLocalPort() const +{ + return std::atoi(getService(impl->fd, true).c_str()); +} + +uint16_t Socket::getRemotePort() const +{ + return atoi(getService(impl->fd, true).c_str()); +} + +int Socket::getError() const +{ + int result; + socklen_t rSize = sizeof (result); + + if (::getsockopt(impl->fd, SOL_SOCKET, SO_ERROR, &result, &rSize) < 0) + throw QPID_POSIX_ERROR(errno); + + return result; +} + +void Socket::setTcpNoDelay(bool nodelay) const +{ + if (nodelay) { + int flag = 1; + int result = setsockopt(impl->fd, IPPROTO_TCP, TCP_NODELAY, (char *)&flag, sizeof(flag)); + QPID_POSIX_CHECK(result); + } +} + +}} // namespace qpid::sys diff --git a/RC5/cpp/src/qpid/sys/posix/StrError.cpp b/RC5/cpp/src/qpid/sys/posix/StrError.cpp new file mode 100644 index 0000000000..633e20213c --- /dev/null +++ b/RC5/cpp/src/qpid/sys/posix/StrError.cpp @@ -0,0 +1,41 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/sys/StrError.h" + +#include <string.h> + +namespace qpid { +namespace sys { + +std::string strError(int err) { + char buf[512] = "Unknown error"; +#ifdef _GNU_SOURCE + // GNU strerror_r returns the message + return ::strerror_r(err, buf, sizeof(buf)); +#else + // POSIX strerror_r doesn't return the buffer + ::strerror_r(err, buf, sizeof(buf)); + return std::string(buf); +#endif +} + +}} diff --git a/RC5/cpp/src/qpid/sys/posix/SystemInfo.cpp b/RC5/cpp/src/qpid/sys/posix/SystemInfo.cpp new file mode 100755 index 0000000000..9bbd023e29 --- /dev/null +++ b/RC5/cpp/src/qpid/sys/posix/SystemInfo.cpp @@ -0,0 +1,97 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/sys/SystemInfo.h" + +#include <sys/ioctl.h> +#include <sys/utsname.h> +#include <net/if.h> +#include <unistd.h> +#include <arpa/inet.h> +#include <stdio.h> +#include <unistd.h> + +#ifndef HOST_NAME_MAX +# define HOST_NAME_MAX 256 +#endif + +using namespace std; + +namespace qpid { +namespace sys { + +long SystemInfo::concurrency() { +#ifdef _SC_NPROCESSORS_ONLN // Linux specific. + return sysconf(_SC_NPROCESSORS_ONLN); +#else + return -1; +#endif +} + +bool SystemInfo::getLocalHostname (TcpAddress &address) { + char name[HOST_NAME_MAX]; + if (::gethostname(name, sizeof(name)) != 0) + return false; + address.host = name; + return true; +} + +static const string LOCALHOST("127.0.0.1"); + +void SystemInfo::getLocalIpAddresses (uint16_t port, + std::vector<Address> &addrList) { + int s = socket (PF_INET, SOCK_STREAM, 0); + for (int i=1;;i++) { + struct ifreq ifr; + ifr.ifr_ifindex = i; + if (::ioctl (s, SIOCGIFNAME, &ifr) < 0) + break; + /* now ifr.ifr_name is set */ + if (::ioctl (s, SIOCGIFADDR, &ifr) < 0) + continue; + struct sockaddr_in *sin = (struct sockaddr_in *) &ifr.ifr_addr; + string addr(inet_ntoa(sin->sin_addr)); + if (addr != LOCALHOST) + addrList.push_back(TcpAddress(addr, port)); + } + if (addrList.empty()) { + addrList.push_back(TcpAddress(LOCALHOST, port)); + } + close (s); +} + +void SystemInfo::getSystemId (std::string &osName, + std::string &nodeName, + std::string &release, + std::string &version, + std::string &machine) +{ + struct utsname _uname; + if (uname (&_uname) == 0) + { + osName = _uname.sysname; + nodeName = _uname.nodename; + release = _uname.release; + version = _uname.version; + machine = _uname.machine; + } +} + +}} // namespace qpid::sys diff --git a/RC5/cpp/src/qpid/sys/posix/Thread.cpp b/RC5/cpp/src/qpid/sys/posix/Thread.cpp new file mode 100644 index 0000000000..bb5641bc53 --- /dev/null +++ b/RC5/cpp/src/qpid/sys/posix/Thread.cpp @@ -0,0 +1,75 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/sys/Thread.h" + +#include "qpid/sys/Runnable.h" +#include "check.h" + +#include <pthread.h> + +namespace qpid { +namespace sys { + +namespace { +void* runRunnable(void* p) +{ + static_cast<Runnable*>(p)->run(); + return 0; +} +} + +struct ThreadPrivate { + pthread_t thread; + + ThreadPrivate(Runnable* runnable) { + QPID_POSIX_ASSERT_THROW_IF(::pthread_create(&thread, NULL, runRunnable, runnable)); + } + + ThreadPrivate() : thread(::pthread_self()) {} +}; + +Thread::Thread() {} + +Thread::Thread(Runnable* runnable) : impl(new ThreadPrivate(runnable)) {} + +Thread::Thread(Runnable& runnable) : impl(new ThreadPrivate(&runnable)) {} + +void Thread::join(){ + if (impl) { + QPID_POSIX_ASSERT_THROW_IF(::pthread_join(impl->thread, 0)); + } +} + +unsigned long Thread::id() { + if (impl) + return impl->thread; + else + return 0; +} + +Thread Thread::current() { + Thread t; + t.impl.reset(new ThreadPrivate()); + return t; +} + +}} diff --git a/RC5/cpp/src/qpid/sys/posix/Time.cpp b/RC5/cpp/src/qpid/sys/posix/Time.cpp new file mode 100644 index 0000000000..d8d0a58d2e --- /dev/null +++ b/RC5/cpp/src/qpid/sys/posix/Time.cpp @@ -0,0 +1,110 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "PrivatePosix.h" + +#include "qpid/sys/Time.h" +#include <ostream> +#include <time.h> +#include <stdio.h> +#include <sys/time.h> +#include <unistd.h> + +namespace { +int64_t max_abstime() { return std::numeric_limits<int64_t>::max(); } +} + +namespace qpid { +namespace sys { + +AbsTime::AbsTime(const AbsTime& t, const Duration& d) : + timepoint(d == Duration::max() ? max_abstime() : t.timepoint+d.nanosecs) +{} + +AbsTime AbsTime::FarFuture() { + AbsTime ff; ff.timepoint = max_abstime(); return ff; +} + +AbsTime AbsTime::now() { + struct timespec ts; + ::clock_gettime(CLOCK_REALTIME, &ts); + AbsTime time_now; + time_now.timepoint = toTime(ts).nanosecs; + return time_now; +} + +Duration::Duration(const AbsTime& time0) : + nanosecs(time0.timepoint) +{} + +Duration::Duration(const AbsTime& start, const AbsTime& finish) : + nanosecs(finish.timepoint - start.timepoint) +{} + +struct timespec& toTimespec(struct timespec& ts, const Duration& t) { + ts.tv_sec = t / TIME_SEC; + ts.tv_nsec = t % TIME_SEC; + return ts; +} + +struct timeval& toTimeval(struct timeval& tv, const Duration& t) { + tv.tv_sec = t/TIME_SEC; + tv.tv_usec = (t%TIME_SEC)/TIME_USEC; + return tv; +} + +Duration toTime(const struct timespec& ts) { + return ts.tv_sec*TIME_SEC + ts.tv_nsec; +} + +std::ostream& operator<<(std::ostream& o, const Duration& d) { + return o << int64_t(d) << "ns"; +} + +std::ostream& operator<<(std::ostream& o, const AbsTime& t) { + static const char * month_abbrevs[] = { + "jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec" + }; + struct tm * timeinfo; + time_t rawtime(t.timepoint/TIME_SEC); + timeinfo = localtime (&rawtime); + char time_string[100]; + sprintf ( time_string, + "%d-%s-%02d %02d:%02d:%02d", + 1900 + timeinfo->tm_year, + month_abbrevs[timeinfo->tm_mon], + timeinfo->tm_mday, + timeinfo->tm_hour, + timeinfo->tm_min, + timeinfo->tm_sec + ); + return o << time_string; +} + +void sleep(int secs) { + ::sleep(secs); +} + +void usleep(uint64_t usecs) { + ::usleep(usecs); +} + +}} diff --git a/RC5/cpp/src/qpid/sys/posix/Time.h b/RC5/cpp/src/qpid/sys/posix/Time.h new file mode 100755 index 0000000000..62d734c816 --- /dev/null +++ b/RC5/cpp/src/qpid/sys/posix/Time.h @@ -0,0 +1,34 @@ +#ifndef QPID_SYS_POSIX_TIME_H +#define QPID_SYS_POSIX_TIME_H + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "qpid/sys/IntegerTypes.h" + +namespace qpid { +namespace sys { + +/** + * Class to represent an instant in time. + */ +typedef int64_t TimePrivate; + +}} // namespace qpid::sys + +#endif /*!QPID_SYS_POSIX_TIME_H*/ diff --git a/RC5/cpp/src/qpid/sys/posix/check.h b/RC5/cpp/src/qpid/sys/posix/check.h new file mode 100644 index 0000000000..f3031b7593 --- /dev/null +++ b/RC5/cpp/src/qpid/sys/posix/check.h @@ -0,0 +1,49 @@ +#ifndef _posix_check_h +#define _posix_check_h + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/Exception.h" + +#include <cerrno> +#include <assert.h> +#include <stdio.h> + +#define QPID_POSIX_ERROR(ERRNO) qpid::Exception(QPID_MSG(qpid::sys::strError(ERRNO))) + +/** THROW QPID_POSIX_ERROR(errno) if RESULT is less than zero */ +#define QPID_POSIX_CHECK(RESULT) \ + if ((RESULT) < 0) throw QPID_POSIX_ERROR((errno)) + +/** Throw a posix error if ERRNO is non-zero */ +#define QPID_POSIX_THROW_IF(ERRNO) \ + do { int e=(ERRNO); if (e) throw QPID_POSIX_ERROR(e); } while(0) + +/** Same as _THROW_IF in a release build, but abort a debug build */ +#ifdef NDEBUG +#define QPID_POSIX_ASSERT_THROW_IF(ERRNO) QPID_POSIX_THROW_IF(ERRNO) +#else +#define QPID_POSIX_ASSERT_THROW_IF(ERRNO) \ + do { int e=(ERRNO); if (e) { errno=e; ::perror(0); assert(0); } } while(0) +#endif + +#endif /*!_posix_check_h*/ diff --git a/RC5/cpp/src/qpid/sys/rdma/RdmaClient.cpp b/RC5/cpp/src/qpid/sys/rdma/RdmaClient.cpp new file mode 100644 index 0000000000..0d3dd83131 --- /dev/null +++ b/RC5/cpp/src/qpid/sys/rdma/RdmaClient.cpp @@ -0,0 +1,207 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "RdmaIO.h" +#include "qpid/sys/Time.h" + +#include <netdb.h> +#include <arpa/inet.h> + +#include <vector> +#include <string> +#include <iostream> +#include <algorithm> +#include <cmath> +#include <boost/bind.hpp> + +using std::vector; +using std::string; +using std::cout; +using std::cerr; +using std::copy; +using std::rand; + +using qpid::sys::Poller; +using qpid::sys::Dispatcher; +using qpid::sys::AbsTime; +using qpid::sys::Duration; +using qpid::sys::TIME_SEC; +using qpid::sys::TIME_INFINITE; + +// count of messages +int64_t smsgs = 0; +int64_t sbytes = 0; +int64_t rmsgs = 0; +int64_t rbytes = 0; + +int target = 1000000; +int msgsize = 200; +AbsTime startTime; +Duration sendingDuration(TIME_INFINITE); +Duration fullTestDuration(TIME_INFINITE); + +vector<char> testString; + +void write(Rdma::AsynchIO& aio) { + while (aio.writable()) { + if (smsgs >= target) + return; + Rdma::Buffer* b = aio.getBuffer(); + std::copy(testString.begin(), testString.end(), b->bytes); + b->dataCount = msgsize; + aio.queueWrite(b); + ++smsgs; + sbytes += msgsize; + } +} + +void dataError(Rdma::AsynchIO&) { + cout << "Data error:\n"; +} + +void data(Poller::shared_ptr p, Rdma::AsynchIO& aio, Rdma::Buffer* b) { + ++rmsgs; + rbytes += b->dataCount; + + // When all messages have been recvd stop + if (rmsgs < target) { + write(aio); + } else { + fullTestDuration = std::min(fullTestDuration, Duration(startTime, AbsTime::now())); + if (aio.incompletedWrites() == 0) + p->shutdown(); + } +} + +void full(Rdma::AsynchIO& a, Rdma::Buffer* b) { + // Warn as we shouldn't get here anymore + cerr << "!"; + + // Don't need to keep buffer just adjust the counts + --smsgs; + sbytes -= b->dataCount; + + // Give buffer back + a.returnBuffer(b); +} + +void idle(Poller::shared_ptr p, Rdma::AsynchIO& aio) { + if (smsgs < target) { + write(aio); + } else { + sendingDuration = std::min(sendingDuration, Duration(startTime, AbsTime::now())); + if (rmsgs >= target && aio.incompletedWrites() == 0) + p->shutdown(); + } +} + +void connected(Poller::shared_ptr poller, Rdma::Connection::intrusive_ptr& ci, const Rdma::ConnectionParams& cp) { + cout << "Connected\n"; + Rdma::QueuePair::intrusive_ptr q = ci->getQueuePair(); + + Rdma::AsynchIO* aio = new Rdma::AsynchIO(ci->getQueuePair(), + cp.maxRecvBufferSize, cp.initialXmitCredit , Rdma::DEFAULT_WR_ENTRIES, + boost::bind(&data, poller, _1, _2), + boost::bind(&idle, poller, _1), + &full, + dataError); + + startTime = AbsTime::now(); + write(*aio); + + aio->start(poller); +} + +void disconnected(boost::shared_ptr<Poller> p, Rdma::Connection::intrusive_ptr&) { + cout << "Disconnected\n"; + p->shutdown(); +} + +void connectionError(boost::shared_ptr<Poller> p, Rdma::Connection::intrusive_ptr&, const Rdma::ErrorType) { + cout << "Connection error\n"; + p->shutdown(); +} + +void rejected(boost::shared_ptr<Poller> p, Rdma::Connection::intrusive_ptr&, const Rdma::ConnectionParams&) { + cout << "Connection rejected\n"; + p->shutdown(); +} + +int main(int argc, char* argv[]) { + vector<string> args(&argv[0], &argv[argc]); + + ::addrinfo *res; + ::addrinfo hints = {}; + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + string port = (args.size() < 3) ? "20079" : args[2]; + int n = ::getaddrinfo(args[1].c_str(), port.c_str(), &hints, &res); + if (n<0) { + cerr << "Can't find information for: " << args[1] << "\n"; + return 1; + } else { + cout << "Connecting to: " << args[1] << ":" << port <<"\n"; + } + + if (args.size() > 3) + msgsize = atoi(args[3].c_str()); + cout << "Message size: " << msgsize << "\n"; + + // Make a random message of that size + testString.resize(msgsize); + for (int i = 0; i < msgsize; ++i) { + testString[i] = 32 + (rand() & 0x3f); + } + + try { + boost::shared_ptr<Poller> p(new Poller()); + Dispatcher d(p); + + Rdma::Connector c( + *res->ai_addr, + Rdma::ConnectionParams(msgsize, Rdma::DEFAULT_WR_ENTRIES), + boost::bind(&connected, p, _1, _2), + boost::bind(&connectionError, p, _1, _2), + boost::bind(&disconnected, p, _1), + boost::bind(&rejected, p, _1, _2)); + + c.start(p); + d.run(); + } catch (Rdma::Exception& e) { + int err = e.getError(); + cerr << "Error: " << e.what() << "(" << err << ")\n"; + } + + cout + << "Sent: " << smsgs + << "msgs (" << sbytes + << "bytes) in: " << double(sendingDuration)/TIME_SEC + << "s: " << double(smsgs)*TIME_SEC/sendingDuration + << "msgs/s(" << double(sbytes)*TIME_SEC/sendingDuration + << "bytes/s)\n"; + cout + << "Recd: " << rmsgs + << "msgs (" << rbytes + << "bytes) in: " << double(fullTestDuration)/TIME_SEC + << "s: " << double(rmsgs)*TIME_SEC/fullTestDuration + << "msgs/s(" << double(rbytes)*TIME_SEC/fullTestDuration + << "bytes/s)\n"; + +} diff --git a/RC5/cpp/src/qpid/sys/rdma/RdmaIO.cpp b/RC5/cpp/src/qpid/sys/rdma/RdmaIO.cpp new file mode 100644 index 0000000000..77e766dd79 --- /dev/null +++ b/RC5/cpp/src/qpid/sys/rdma/RdmaIO.cpp @@ -0,0 +1,617 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "RdmaIO.h" + +#include "qpid/log/Statement.h" + + +#include <iostream> +#include <boost/bind.hpp> + +using qpid::sys::DispatchHandle; +using qpid::sys::Poller; + +namespace Rdma { + AsynchIO::AsynchIO( + QueuePair::intrusive_ptr q, + int size, + int xCredit, + int rCount, + ReadCallback rc, + IdleCallback ic, + FullCallback fc, + ErrorCallback ec + ) : + qp(q), + dataHandle(*qp, boost::bind(&AsynchIO::dataEvent, this, _1), 0, 0), + bufferSize(size), + recvCredit(0), + xmitCredit(xCredit), + recvBufferCount(rCount), + xmitBufferCount(xCredit), + outstandingWrites(0), + closed(false), + deleting(false), + state(IDLE), + readCallback(rc), + idleCallback(ic), + fullCallback(fc), + errorCallback(ec) + { + qp->nonblocking(); + qp->notifyRecv(); + qp->notifySend(); + + // Prepost some recv buffers before we go any further + for (int i = 0; i<recvBufferCount; ++i) { + // Allocate recv buffer + Buffer* b = qp->createBuffer(bufferSize); + buffers.push_front(b); + b->dataCount = b->byteCount; + qp->postRecv(b); + } + + for (int i = 0; i<xmitBufferCount; ++i) { + // Allocate xmit buffer + Buffer* b = qp->createBuffer(bufferSize); + buffers.push_front(b); + bufferQueue.push_front(b); + b->dataCount = 0; + b->dataStart = 0; + } + } + + AsynchIO::~AsynchIO() { + // Warn if we are deleting whilst there are still unreclaimed write buffers + if ( outstandingWrites>0 ) + QPID_LOG(error, "RDMA: qp=" << qp << ": Deleting queue before all write buffers finished"); + + // Turn off callbacks (before doing the deletes) + dataHandle.stopWatch(); + + // The buffers ptr_deque automatically deletes all the buffers we've allocated + // TODO: It might turn out to be more efficient in high connection loads to reuse the + // buffers rather than having to reregister them all the time (this would be straightforward if all + // connections haver the same buffer size and harder otherwise) + } + + void AsynchIO::start(Poller::shared_ptr poller) { + dataHandle.startWatch(poller); + } + + // Mark for deletion/Delete this object when we have no outstanding writes + void AsynchIO::deferDelete() { + State oldState; + State newState; + bool doReturn; + //qpid::sys::ScopedLock<qpid::sys::Mutex> l(stateLock); + // It is safe to assign to deleting here as we either delete ourselves + // before leaving this function or deleting is set on exit + do { + newState = oldState = state.get(); + doReturn = false; + if (outstandingWrites > 0 || oldState != IDLE) { + deleting = true; + doReturn = true; + } else{ + newState = DELETED; // Stop any read callback before the dataHandle.stopWatch() in the destructor + } + } while (!state.boolCompareAndSwap(oldState, newState)); + if (doReturn) { + return; + } + delete this; + } + + void AsynchIO::queueWrite(Buffer* buff) { + // Make sure we don't overrun our available buffers + // either at our end or the known available at the peers end + if (writable()) { + // TODO: We might want to batch up sending credit + if (recvCredit > 0) { + int creditSent = recvCredit & ~FlagsMask; + qp->postSend(creditSent, buff); + recvCredit -= creditSent; + } else { + qp->postSend(buff); + } + ++outstandingWrites; + --xmitCredit; + } else { + if (fullCallback) { + fullCallback(*this, buff); + } else { + QPID_LOG(error, "RDMA: qp=" << qp << ": Write queue full, but no callback, throwing buffer away"); + returnBuffer(buff); + } + } + } + + // Mark now closed (so we don't accept any more writes or make any idle callbacks) + void AsynchIO::queueWriteClose() { + // Don't think we actually need to lock here as transition is 1 way only to closed + closed = true; + } + + void AsynchIO::notifyPendingWrite() { + // As notifyPendingWrite can be called on an arbitrary thread it must check whether we are processing or not. + // If we are then we just return as we know that we will eventually do the idle callback anyway. + // + // qpid::sys::ScopedLock<qpid::sys::Mutex> l(stateLock); + // We can get here in any state (as the caller could be in any thread) + State oldState; + State newState; + bool doReturn; + do { + newState = oldState = state.get(); + doReturn = false; + switch (oldState) { + case NOTIFY_WRITE: + case PENDING_NOTIFY: + // We only need to note a pending notify if we're already doing a notify as data processing + // is always followed by write notification processing + newState = PENDING_NOTIFY; + doReturn = true; + break; + case PENDING_DATA: + doReturn = true; + break; + case DATA: + // Only need to return here as data processing will do the idleCallback itself anyway + doReturn = true; + break; + case IDLE: + newState = NOTIFY_WRITE; + break; + case DELETED: + assert(oldState!=DELETED); + doReturn = true; + }; + } while (!state.boolCompareAndSwap(oldState, newState)); + if (doReturn) { + return; + } + + doWriteCallback(); + + // Keep track of what we need to do so that we can release the lock + enum {COMPLETION, NOTIFY, RETURN, EXIT} action; + // If there was pending data whilst we were doing this, process it now + // + // Using NOTIFY_WRITE for both NOTIFY & COMPLETION is a bit strange, but we're making sure we get the + // correct result if we reenter notifyPendingWrite(), in which case we want to + // end up in PENDING_NOTIFY (entering dataEvent doesn't matter as it only checks + // not IDLE) + do { + //qpid::sys::ScopedLock<qpid::sys::Mutex> l(stateLock); + do { + newState = oldState = state.get(); + action = RETURN; // Anything but COMPLETION + switch (oldState) { + case NOTIFY_WRITE: + newState = IDLE; + action = (action == COMPLETION) ? EXIT : RETURN; + break; + case PENDING_DATA: + newState = NOTIFY_WRITE; + action = COMPLETION; + break; + case PENDING_NOTIFY: + newState = NOTIFY_WRITE; + action = NOTIFY; + break; + default: + assert(oldState!=IDLE && oldState!=DATA && oldState!=DELETED); + action = RETURN; + } + } while (!state.boolCompareAndSwap(oldState, newState)); + + // Note we only get here if we were in the PENDING_DATA or PENDING_NOTIFY state + // so that we do need to process completions or notifications now + switch (action) { + case COMPLETION: + processCompletions(); + // Fall through + case NOTIFY: + doWriteCallback(); + break; + case RETURN: + return; + case EXIT: + // If we just processed completions we might need to delete ourselves + if (deleting && outstandingWrites == 0) { + delete this; + } + return; + } + } while (true); + } + + void AsynchIO::dataEvent(qpid::sys::DispatchHandle&) { + // Keep track of writable notifications + // qpid::sys::ScopedLock<qpid::sys::Mutex> l(stateLock); + State oldState; + State newState; + bool doReturn; + do { + newState = oldState = state.get(); + doReturn = false; + // We're already processing a notification + switch (oldState) { + case IDLE: + newState = DATA; + break; + default: + // Can't get here in DATA state as that would violate the serialisation rules + assert( oldState!=DATA ); + newState = PENDING_DATA; + doReturn = true; + } + } while (!state.boolCompareAndSwap(oldState, newState)); + if (doReturn) { + return; + } + + processCompletions(); + + //qpid::sys::ScopedLock<qpid::sys::Mutex> l(stateLock); + do { + newState = oldState = state.get(); + assert( oldState==DATA ); + newState = NOTIFY_WRITE; + } while (!state.boolCompareAndSwap(oldState, newState)); + + do { + doWriteCallback(); + + // qpid::sys::ScopedLock<qpid::sys::Mutex> l(stateLock); + bool doBreak; + do { + newState = oldState = state.get(); + doBreak = false; + if ( oldState==NOTIFY_WRITE ) { + newState = IDLE; + doBreak = true; + } else { + // Can't get DATA/PENDING_DATA here as dataEvent cannot be reentered + assert( oldState==PENDING_NOTIFY ); + newState = NOTIFY_WRITE; + } + } while (!state.boolCompareAndSwap(oldState, newState)); + if (doBreak) { + break; + } + } while (true); + + // We might need to delete ourselves + if (deleting && outstandingWrites == 0) { + delete this; + } + } + + void AsynchIO::processCompletions() { + QueuePair::intrusive_ptr q = qp->getNextChannelEvent(); + + // Re-enable notification for queue: + // This needs to happen before we could do anything that could generate more work completion + // events (ie the callbacks etc. in the following). + // This can't make us reenter this code as the handle attached to the completion queue will still be + // disabled by the poller until we leave this code + qp->notifyRecv(); + qp->notifySend(); + + int recvEvents = 0; + int sendEvents = 0; + + // If no event do nothing + if (!q) + return; + + assert(q == qp); + + // Repeat until no more events + do { + QueuePairEvent e(qp->getNextEvent()); + if (!e) + break; + + ::ibv_wc_status status = e.getEventStatus(); + if (status != IBV_WC_SUCCESS) { + errorCallback(*this); + // TODO: Probably need to flush queues at this point + return; + } + + // Test if recv (or recv with imm) + //::ibv_wc_opcode eventType = e.getEventType(); + Buffer* b = e.getBuffer(); + QueueDirection dir = e.getDirection(); + if (dir == RECV) { + ++recvEvents; + + // Get our xmitCredit if it was sent + bool dataPresent = true; + if (e.immPresent() ) { + xmitCredit += (e.getImm() & ~FlagsMask); + dataPresent = ((e.getImm() & IgnoreData) == 0); + } + + // if there was no data sent then the message was only to update our credit + if ( dataPresent ) { + readCallback(*this, b); + } + + // At this point the buffer has been consumed so put it back on the recv queue + b->dataStart = 0; + b->dataCount = 0; + qp->postRecv(b); + + // Received another message + ++recvCredit; + + // Send recvCredit if it is large enough (it will have got this large because we've not sent anything recently) + if (recvCredit > recvBufferCount/2) { + // TODO: This should use RDMA write with imm as there might not ever be a buffer to receive this message + // but this is a little unlikely, as to get in this state we have to have received messages without sending any + // for a while so its likely we've received an credit update from the far side. + if (writable()) { + Buffer* ob = getBuffer(); + // Have to send something as adapters hate it when you try to transfer 0 bytes + *reinterpret_cast< uint32_t* >(ob->bytes) = htonl(recvCredit); + ob->dataCount = sizeof(uint32_t); + + int creditSent = recvCredit & ~FlagsMask; + qp->postSend(creditSent | IgnoreData, ob); + recvCredit -= creditSent; + ++outstandingWrites; + --xmitCredit; + } else { + QPID_LOG(warning, "RDMA: qp=" << qp << ": Unable to send unsolicited credit"); + } + } + } else { + ++sendEvents; + { + qpid::sys::ScopedLock<qpid::sys::Mutex> l(bufferQueueLock); + bufferQueue.push_front(b); + } + --outstandingWrites; + } + } while (true); + + // Not sure if this is expected or not + if (recvEvents == 0 && sendEvents == 0) { + QPID_LOG(debug, "RDMA: qp=" << qp << ": Got channel event with no recv/send completions"); + } + } + + void AsynchIO::doWriteCallback() { + // TODO: maybe don't call idle unless we're low on write buffers + // Keep on calling the idle routine as long as we are writable and we got something to write last call + while (writable()) { + int xc = xmitCredit; + idleCallback(*this); + // Check whether we actually wrote anything + if (xmitCredit == xc) { + QPID_LOG(debug, "RDMA: qp=" << qp << ": Called for data, but got none: xmitCredit=" << xmitCredit); + return; + } + } + } + + Buffer* AsynchIO::getBuffer() { + qpid::sys::ScopedLock<qpid::sys::Mutex> l(bufferQueueLock); + assert(!bufferQueue.empty()); + Buffer* b = bufferQueue.front(); + bufferQueue.pop_front(); + b->dataCount = 0; + b->dataStart = 0; + return b; + } + + void AsynchIO::returnBuffer(Buffer* b) { + qpid::sys::ScopedLock<qpid::sys::Mutex> l(bufferQueueLock); + bufferQueue.push_front(b); + b->dataCount = 0; + b->dataStart = 0; + } + + ConnectionManager::ConnectionManager( + ErrorCallback errc, + DisconnectedCallback dc + ) : + ci(Connection::make()), + handle(*ci, boost::bind(&ConnectionManager::event, this, _1), 0, 0), + errorCallback(errc), + disconnectedCallback(dc) + { + ci->nonblocking(); + } + + void ConnectionManager::start(Poller::shared_ptr poller) { + startConnection(ci); + handle.startWatch(poller); + } + + void ConnectionManager::event(DispatchHandle&) { + connectionEvent(ci); + } + + Listener::Listener( + const sockaddr& src, + const ConnectionParams& cp, + EstablishedCallback ec, + ErrorCallback errc, + DisconnectedCallback dc, + ConnectionRequestCallback crc + ) : + ConnectionManager(errc, dc), + src_addr(src), + checkConnectionParams(cp), + connectionRequestCallback(crc), + establishedCallback(ec) + { + } + + void Listener::startConnection(Connection::intrusive_ptr ci) { + ci->bind(src_addr); + ci->listen(); + } + + void Listener::connectionEvent(Connection::intrusive_ptr ci) { + ConnectionEvent e(ci->getNextEvent()); + + // If (for whatever reason) there was no event do nothing + if (!e) + return; + + // Important documentation ommision the new rdma_cm_id + // you get from CONNECT_REQUEST has the same context info + // as its parent listening rdma_cm_id + ::rdma_cm_event_type eventType = e.getEventType(); + ::rdma_conn_param conn_param = e.getConnectionParam(); + Rdma::Connection::intrusive_ptr id = e.getConnection(); + + switch (eventType) { + case RDMA_CM_EVENT_CONNECT_REQUEST: { + // Make sure peer has sent params we can use + if (!conn_param.private_data || conn_param.private_data_len < sizeof(ConnectionParams)) { + id->reject(); + break; + } + ConnectionParams cp = *static_cast<const ConnectionParams*>(conn_param.private_data); + + // Reject if requested msg size is bigger than we allow + if (cp.maxRecvBufferSize > checkConnectionParams.maxRecvBufferSize) { + id->reject(&checkConnectionParams); + break; + } + + bool accept = true; + if (connectionRequestCallback) + accept = connectionRequestCallback(id, cp); + + if (accept) { + // Accept connection + cp.initialXmitCredit = checkConnectionParams.initialXmitCredit; + id->accept(conn_param, &cp); + } else { + // Reject connection + id->reject(); + } + break; + } + case RDMA_CM_EVENT_ESTABLISHED: + establishedCallback(id); + break; + case RDMA_CM_EVENT_DISCONNECTED: + disconnectedCallback(id); + break; + case RDMA_CM_EVENT_CONNECT_ERROR: + errorCallback(id, CONNECT_ERROR); + break; + default: + // Unexpected response + errorCallback(id, UNKNOWN); + //std::cerr << "Warning: unexpected response to listen - " << eventType << "\n"; + } + } + + Connector::Connector( + const sockaddr& dst, + const ConnectionParams& cp, + ConnectedCallback cc, + ErrorCallback errc, + DisconnectedCallback dc, + RejectedCallback rc + ) : + ConnectionManager(errc, dc), + dst_addr(dst), + connectionParams(cp), + rejectedCallback(rc), + connectedCallback(cc) + { + } + + void Connector::startConnection(Connection::intrusive_ptr ci) { + ci->resolve_addr(dst_addr); + } + + void Connector::connectionEvent(Connection::intrusive_ptr ci) { + ConnectionEvent e(ci->getNextEvent()); + + // If (for whatever reason) there was no event do nothing + if (!e) + return; + + ::rdma_cm_event_type eventType = e.getEventType(); + ::rdma_conn_param conn_param = e.getConnectionParam(); + Rdma::Connection::intrusive_ptr id = e.getConnection(); + switch (eventType) { + case RDMA_CM_EVENT_ADDR_RESOLVED: + // RESOLVE_ADDR + ci->resolve_route(); + break; + case RDMA_CM_EVENT_ADDR_ERROR: + // RESOLVE_ADDR + errorCallback(ci, ADDR_ERROR); + break; + case RDMA_CM_EVENT_ROUTE_RESOLVED: + // RESOLVE_ROUTE: + ci->connect(&connectionParams); + break; + case RDMA_CM_EVENT_ROUTE_ERROR: + // RESOLVE_ROUTE: + errorCallback(ci, ROUTE_ERROR); + break; + case RDMA_CM_EVENT_CONNECT_ERROR: + // CONNECTING + errorCallback(ci, CONNECT_ERROR); + break; + case RDMA_CM_EVENT_UNREACHABLE: + // CONNECTING + errorCallback(ci, UNREACHABLE); + break; + case RDMA_CM_EVENT_REJECTED: { + // CONNECTING + // Extract private data from event + assert(conn_param.private_data && conn_param.private_data_len >= sizeof(ConnectionParams)); + ConnectionParams cp = *static_cast<const ConnectionParams*>(conn_param.private_data); + rejectedCallback(ci, cp); + break; + } + case RDMA_CM_EVENT_ESTABLISHED: { + // CONNECTING + // Extract private data from event + assert(conn_param.private_data && conn_param.private_data_len >= sizeof(ConnectionParams)); + ConnectionParams cp = *static_cast<const ConnectionParams*>(conn_param.private_data); + connectedCallback(ci, cp); + break; + } + case RDMA_CM_EVENT_DISCONNECTED: + // ESTABLISHED + disconnectedCallback(ci); + break; + default: + QPID_LOG(warning, "RDMA: Unexpected event in connect: " << eventType); + } + } +} diff --git a/RC5/cpp/src/qpid/sys/rdma/RdmaIO.h b/RC5/cpp/src/qpid/sys/rdma/RdmaIO.h new file mode 100644 index 0000000000..577c22d053 --- /dev/null +++ b/RC5/cpp/src/qpid/sys/rdma/RdmaIO.h @@ -0,0 +1,222 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef Rdma_Acceptor_h +#define Rdma_Acceptor_h + +#include "rdma_wrap.h" + +#include "qpid/sys/AtomicValue.h" +#include "qpid/sys/Dispatcher.h" +#include "qpid/sys/DispatchHandle.h" +#include "qpid/sys/Mutex.h" + +#include <netinet/in.h> + +#include <boost/function.hpp> +#include <boost/ptr_container/ptr_deque.hpp> +#include <deque> + +namespace Rdma { + + class Connection; + + class AsynchIO + { + typedef boost::function1<void, AsynchIO&> ErrorCallback; + typedef boost::function2<void, AsynchIO&, Buffer*> ReadCallback; + typedef boost::function1<void, AsynchIO&> IdleCallback; + typedef boost::function2<void, AsynchIO&, Buffer*> FullCallback; + + QueuePair::intrusive_ptr qp; + qpid::sys::DispatchHandleRef dataHandle; + int bufferSize; + int recvCredit; + int xmitCredit; + int recvBufferCount; + int xmitBufferCount; + int outstandingWrites; + bool closed; // TODO: Perhaps (probably) this state can be merged with the following... + bool deleting; // TODO: Perhaps (probably) this state can be merged with the following... + enum State { IDLE, DATA, PENDING_DATA, NOTIFY_WRITE, PENDING_NOTIFY, DELETED }; + qpid::sys::AtomicValue<State> state; + //qpid::sys::Mutex stateLock; + std::deque<Buffer*> bufferQueue; + qpid::sys::Mutex bufferQueueLock; + boost::ptr_deque<Buffer> buffers; + + ReadCallback readCallback; + IdleCallback idleCallback; + FullCallback fullCallback; + ErrorCallback errorCallback; + + public: + // TODO: Instead of specifying a buffer size specify the amount of memory the AsynchIO class can use + // for buffers both read and write (allocate half to each up front) and fail if we cannot allocate that much + // locked memory + AsynchIO( + QueuePair::intrusive_ptr q, + int size, + int xCredit, + int rCount, + ReadCallback rc, + IdleCallback ic, + FullCallback fc, + ErrorCallback ec + ); + + void start(qpid::sys::Poller::shared_ptr poller); + bool writable() const; + bool bufferAvailable() const; + void queueWrite(Buffer* buff); + void notifyPendingWrite(); + void queueWriteClose(); + void deferDelete(); + int incompletedWrites() const; + Buffer* getBuffer(); + void returnBuffer(Buffer*); + + private: + // Don't let anyone else delete us to make sure there can't be outstanding callbacks + ~AsynchIO(); + + // Constants for the peer-peer command messages + // These are sent in the high bits if the imm data of an rdma message + // The low bits are used to send the credit + const static int FlagsMask = 0x10000000; // Mask for all flag bits - be sure to update this if you add more command bits + const static int IgnoreData = 0x10000000; // Message contains no application data + + void dataEvent(qpid::sys::DispatchHandle& handle); + void processCompletions(); + void doWriteCallback(); + }; + + inline bool AsynchIO::writable() const { + return (!closed && outstandingWrites < xmitBufferCount && xmitCredit > 0); + } + + inline int AsynchIO::incompletedWrites() const { + return outstandingWrites; + } + + inline bool AsynchIO::bufferAvailable() const { + return !bufferQueue.empty(); + } + // These are the parameters necessary to start the conversation + // * Each peer HAS to allocate buffers of the size of the maximum receive from its peer + // * Each peer HAS to know the initial "credit" it has for transmitting to its peer + struct ConnectionParams { + int maxRecvBufferSize; + int initialXmitCredit ; + + ConnectionParams(int s, int c) : + maxRecvBufferSize(s), + initialXmitCredit(c) + {} + }; + + enum ErrorType { + ADDR_ERROR, + ROUTE_ERROR, + CONNECT_ERROR, + UNREACHABLE, + UNKNOWN + }; + + typedef boost::function2<void, Rdma::Connection::intrusive_ptr&, ErrorType> ErrorCallback; + typedef boost::function1<void, Rdma::Connection::intrusive_ptr&> DisconnectedCallback; + + class ConnectionManager { + Connection::intrusive_ptr ci; + qpid::sys::DispatchHandle handle; + + protected: + ErrorCallback errorCallback; + DisconnectedCallback disconnectedCallback; + + public: + ConnectionManager( + ErrorCallback errc, + DisconnectedCallback dc + ); + + virtual ~ConnectionManager() {} + + void start(qpid::sys::Poller::shared_ptr poller); + + private: + void event(qpid::sys::DispatchHandle& handle); + + virtual void startConnection(Connection::intrusive_ptr ci) = 0; + virtual void connectionEvent(Connection::intrusive_ptr ci) = 0; + }; + + typedef boost::function2<bool, Rdma::Connection::intrusive_ptr&, const ConnectionParams&> ConnectionRequestCallback; + typedef boost::function1<void, Rdma::Connection::intrusive_ptr&> EstablishedCallback; + + class Listener : public ConnectionManager + { + sockaddr src_addr; + ConnectionParams checkConnectionParams; + ConnectionRequestCallback connectionRequestCallback; + EstablishedCallback establishedCallback; + + public: + Listener( + const sockaddr& src, + const ConnectionParams& cp, + EstablishedCallback ec, + ErrorCallback errc, + DisconnectedCallback dc, + ConnectionRequestCallback crc = 0 + ); + + private: + void startConnection(Connection::intrusive_ptr ci); + void connectionEvent(Connection::intrusive_ptr ci); + }; + + typedef boost::function2<void, Rdma::Connection::intrusive_ptr&, const ConnectionParams&> RejectedCallback; + typedef boost::function2<void, Rdma::Connection::intrusive_ptr&, const ConnectionParams&> ConnectedCallback; + + class Connector : public ConnectionManager + { + sockaddr dst_addr; + ConnectionParams connectionParams; + RejectedCallback rejectedCallback; + ConnectedCallback connectedCallback; + + public: + Connector( + const sockaddr& dst, + const ConnectionParams& cp, + ConnectedCallback cc, + ErrorCallback errc, + DisconnectedCallback dc, + RejectedCallback rc = 0 + ); + + private: + void startConnection(Connection::intrusive_ptr ci); + void connectionEvent(Connection::intrusive_ptr ci); + }; +} + +#endif // Rdma_Acceptor_h diff --git a/RC5/cpp/src/qpid/sys/rdma/RdmaServer.cpp b/RC5/cpp/src/qpid/sys/rdma/RdmaServer.cpp new file mode 100644 index 0000000000..594578a265 --- /dev/null +++ b/RC5/cpp/src/qpid/sys/rdma/RdmaServer.cpp @@ -0,0 +1,167 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "RdmaIO.h" + +#include <arpa/inet.h> + +#include <vector> +#include <queue> +#include <string> +#include <iostream> + +#include <boost/bind.hpp> + +using std::vector; +using std::queue; +using std::string; +using std::cout; +using std::cerr; + +using qpid::sys::Poller; +using qpid::sys::Dispatcher; + +// All the accepted connections +struct ConRec { + Rdma::Connection::intrusive_ptr connection; + Rdma::AsynchIO* data; + queue<Rdma::Buffer*> queuedWrites; + + ConRec(Rdma::Connection::intrusive_ptr c) : + connection(c) + {} +}; + +void dataError(Rdma::AsynchIO&) { + cout << "Data error:\n"; +} + +void idle(ConRec* cr, Rdma::AsynchIO& a) { + // Need to make sure full is not called as it would reorder messages + while (!cr->queuedWrites.empty() && a.writable()) { + Rdma::Buffer* buf = cr->queuedWrites.front(); + cr->queuedWrites.pop(); + a.queueWrite(buf); + } +} + +void data(ConRec* cr, Rdma::AsynchIO& a, Rdma::Buffer* b) { + // Echo data back + Rdma::Buffer* buf = a.getBuffer(); + std::copy(b->bytes+b->dataStart, b->bytes+b->dataStart+b->dataCount, buf->bytes); + buf->dataCount = b->dataCount; + if (cr->queuedWrites.empty()) { + // If can't write then full will be called and push buffer on back of queue + a.queueWrite(buf); + } else { + cr->queuedWrites.push(buf); + // Try to empty queue + idle(cr, a); + } +} + +void full(ConRec* cr, Rdma::AsynchIO&, Rdma::Buffer* buf) { + cr->queuedWrites.push(buf); +} + +void disconnected(Rdma::Connection::intrusive_ptr& ci) { + ConRec* cr = ci->getContext<ConRec>(); + cr->connection->disconnect(); + cr->data->queueWriteClose(); + delete cr; + cout << "Disconnected: " << cr << "\n"; +} + +void connectionError(Rdma::Connection::intrusive_ptr& ci, Rdma::ErrorType) { + ConRec* cr = ci->getContext<ConRec>(); + cr->connection->disconnect(); + if (cr) { + cr->data->queueWriteClose(); + delete cr; + } + cout << "Connection error: " << cr << "\n"; +} + +bool connectionRequest(Rdma::Connection::intrusive_ptr& ci, const Rdma::ConnectionParams& cp) { + cout << "Incoming connection: "; + + // For fun reject alternate connection attempts + static bool x = false; + x = true; + + // Must create aio here so as to prepost buffers *before* we accept connection + if (x) { + ConRec* cr = new ConRec(ci); + Rdma::AsynchIO* aio = + new Rdma::AsynchIO(ci->getQueuePair(), + cp.maxRecvBufferSize, cp.initialXmitCredit, Rdma::DEFAULT_WR_ENTRIES, + boost::bind(data, cr, _1, _2), + boost::bind(idle, cr, _1), + boost::bind(full, cr, _1, _2), + dataError); + ci->addContext(cr); + cr->data = aio; + cout << "Accept=>" << cr << "\n"; + } else { + cout << "Reject\n"; + } + + return x; +} + +void connected(Poller::shared_ptr poller, Rdma::Connection::intrusive_ptr& ci) { + static int cnt = 0; + ConRec* cr = ci->getContext<ConRec>(); + cout << "Connected: " << cr << "(" << ++cnt << ")\n"; + + cr->data->start(poller); +} + +int main(int argc, char* argv[]) { + vector<string> args(&argv[0], &argv[argc]); + + ::sockaddr_in sin; + + int port = (args.size() < 2) ? 20079 : atoi(args[1].c_str()); + cout << "Listening on port: " << port << "\n"; + + sin.sin_family = AF_INET; + sin.sin_port = htons(port); + sin.sin_addr.s_addr = INADDR_ANY; + + try { + boost::shared_ptr<Poller> p(new Poller()); + Dispatcher d(p); + + Rdma::Listener a((const sockaddr&)(sin), + Rdma::ConnectionParams(16384, Rdma::DEFAULT_WR_ENTRIES), + boost::bind(connected, p, _1), + connectionError, + disconnected, + connectionRequest); + + + a.start(p); + d.run(); + } catch (Rdma::Exception& e) { + int err = e.getError(); + cerr << "Error: " << e.what() << "(" << err << ")\n"; + } +} diff --git a/RC5/cpp/src/qpid/sys/rdma/rdma_exception.h b/RC5/cpp/src/qpid/sys/rdma/rdma_exception.h new file mode 100644 index 0000000000..7867aef2e4 --- /dev/null +++ b/RC5/cpp/src/qpid/sys/rdma/rdma_exception.h @@ -0,0 +1,65 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef RDMA_EXCEPTION_H +#define RDMA_EXCEPTION_H + +#include <exception> + +#include <errno.h> +#include <string.h> + +namespace Rdma { + static __thread char s[50]; + class Exception : public std::exception { + int err; + + public: + Exception(int e) : err(e) {} + int getError() { return err; } + const char* what() const throw() { + return ::strerror_r(err, s, 50); + } + }; + + inline void THROW_ERRNO() { + throw Rdma::Exception(errno); + } + + inline void CHECK(int rc) { + if (rc != 0) + throw Rdma::Exception((rc == -1) ? errno : rc >0 ? rc : -rc); + } + + inline void CHECK_IBV(int rc) { + if (rc != 0) + throw Rdma::Exception(rc); + } + + template <typename T> + inline + T* CHECK_NULL(T* rc) { + if (rc == 0) + THROW_ERRNO(); + return rc; + } +} + +#endif // RDMA_EXCEPTION_H diff --git a/RC5/cpp/src/qpid/sys/rdma/rdma_factories.cpp b/RC5/cpp/src/qpid/sys/rdma/rdma_factories.cpp new file mode 100644 index 0000000000..c6e8df814b --- /dev/null +++ b/RC5/cpp/src/qpid/sys/rdma/rdma_factories.cpp @@ -0,0 +1,64 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "rdma_factories.h" + +namespace Rdma { + void acker(::rdma_cm_event* e) throw () { + if (e) + // Intentionally ignore return value - we can't do anything about it here + (void) ::rdma_ack_cm_event(e); + } + + void destroyEChannel(::rdma_event_channel* c) throw () { + if (c) + // Intentionally ignore return value - we can't do anything about it here + (void) ::rdma_destroy_event_channel(c); + } + + void destroyId(::rdma_cm_id* i) throw () { + if (i) + // Intentionally ignore return value - we can't do anything about it here + (void) ::rdma_destroy_id(i); + } + + void deallocPd(::ibv_pd* p) throw () { + if (p) + // Intentionally ignore return value - we can't do anything about it here + (void) ::ibv_dealloc_pd(p); + } + + void destroyCChannel(::ibv_comp_channel* c) throw () { + if (c) + // Intentionally ignore return value - we can't do anything about it here + (void) ::ibv_destroy_comp_channel(c); + } + + void destroyCq(::ibv_cq* cq) throw () { + if (cq) + (void) ::ibv_destroy_cq(cq); + } + + void destroyQp(::ibv_qp* qp) throw () { + if (qp) + (void) ::ibv_destroy_qp(qp); + } + +} diff --git a/RC5/cpp/src/qpid/sys/rdma/rdma_factories.h b/RC5/cpp/src/qpid/sys/rdma/rdma_factories.h new file mode 100644 index 0000000000..8d024f37aa --- /dev/null +++ b/RC5/cpp/src/qpid/sys/rdma/rdma_factories.h @@ -0,0 +1,69 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef RDMA_FACTORIES_H +#define RDMA_FACTORIES_H + +#include "rdma_exception.h" + +#include <rdma/rdma_cma.h> + +#include <boost/shared_ptr.hpp> + +namespace Rdma { + // These allow us to use simple shared_ptrs to do ref counting + void acker(::rdma_cm_event* e) throw (); + void destroyEChannel(::rdma_event_channel* c) throw (); + void destroyId(::rdma_cm_id* i) throw (); + void deallocPd(::ibv_pd* p) throw (); + void destroyCChannel(::ibv_comp_channel* c) throw (); + void destroyCq(::ibv_cq* cq) throw (); + void destroyQp(::ibv_qp* qp) throw (); + + inline boost::shared_ptr< ::rdma_event_channel > mkEChannel() { + ::rdma_event_channel* c = CHECK_NULL(::rdma_create_event_channel()); + return boost::shared_ptr< ::rdma_event_channel >(c, destroyEChannel); + } + + inline boost::shared_ptr< ::rdma_cm_id > + mkId(::rdma_event_channel* ec, void* context, ::rdma_port_space ps) { + ::rdma_cm_id* i; + CHECK(::rdma_create_id(ec, &i, context, ps)); + return boost::shared_ptr< ::rdma_cm_id >(i, destroyId); + } + + inline boost::shared_ptr< ::ibv_pd > allocPd(::ibv_context* c) { + ::ibv_pd* pd = CHECK_NULL(ibv_alloc_pd(c)); + return boost::shared_ptr< ::ibv_pd >(pd, deallocPd); + } + + inline boost::shared_ptr< ::ibv_comp_channel > mkCChannel(::ibv_context* c) { + ::ibv_comp_channel* cc = CHECK_NULL(::ibv_create_comp_channel(c)); + return boost::shared_ptr< ::ibv_comp_channel >(cc, destroyCChannel); + } + + inline boost::shared_ptr< ::ibv_cq > + mkCq(::ibv_context* c, int cqe, void* context, ::ibv_comp_channel* cc) { + ::ibv_cq* cq = CHECK_NULL(ibv_create_cq(c, cqe, context, cc, 0)); + return boost::shared_ptr< ::ibv_cq >(cq, destroyCq); + } +} + +#endif // RDMA_FACTORIES_H diff --git a/RC5/cpp/src/qpid/sys/rdma/rdma_wrap.cpp b/RC5/cpp/src/qpid/sys/rdma/rdma_wrap.cpp new file mode 100644 index 0000000000..e105eb68c6 --- /dev/null +++ b/RC5/cpp/src/qpid/sys/rdma/rdma_wrap.cpp @@ -0,0 +1,183 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "rdma_wrap.h" + +namespace Rdma { + const ::rdma_conn_param DEFAULT_CONNECT_PARAM = { + 0, // .private_data + 0, // .private_data_len + 4, // .responder_resources + 4, // .initiator_depth + 0, // .flow_control + 5, // .retry_count + 7 // .rnr_retry_count + }; + + // This is moderately inefficient so don't use in a critical path + int deviceCount() { + int count; + ::ibv_free_device_list(::ibv_get_device_list(&count)); + return count; + } + + ::rdma_conn_param ConnectionEvent::getConnectionParam() const { + // It's badly documented, but it seems from the librdma source code that all the following + // event types have a valid param.conn + switch (event->event) { + case RDMA_CM_EVENT_CONNECT_REQUEST: + case RDMA_CM_EVENT_ESTABLISHED: + case RDMA_CM_EVENT_REJECTED: + case RDMA_CM_EVENT_DISCONNECTED: + case RDMA_CM_EVENT_CONNECT_ERROR: + return event->param.conn; + default: + ::rdma_conn_param p = {}; + return p; + } + } + + QueuePair::QueuePair(boost::shared_ptr< ::rdma_cm_id > i) : + qpid::sys::IOHandle(new qpid::sys::IOHandlePrivate), + pd(allocPd(i->verbs)), + cchannel(mkCChannel(i->verbs)), + scq(mkCq(i->verbs, DEFAULT_CQ_ENTRIES, 0, cchannel.get())), + rcq(mkCq(i->verbs, DEFAULT_CQ_ENTRIES, 0, cchannel.get())), + outstandingSendEvents(0), + outstandingRecvEvents(0) + { + impl->fd = cchannel->fd; + + // Set cq context to this QueuePair object so we can find + // ourselves again + scq->cq_context = this; + rcq->cq_context = this; + + ::ibv_qp_init_attr qp_attr = {}; + + // TODO: make a default struct for this + qp_attr.cap.max_send_wr = DEFAULT_WR_ENTRIES; + qp_attr.cap.max_send_sge = 4; + qp_attr.cap.max_recv_wr = DEFAULT_WR_ENTRIES; + qp_attr.cap.max_recv_sge = 4; + + qp_attr.send_cq = scq.get(); + qp_attr.recv_cq = rcq.get(); + qp_attr.qp_type = IBV_QPT_RC; + + CHECK(::rdma_create_qp(i.get(), pd.get(), &qp_attr)); + qp = boost::shared_ptr< ::ibv_qp >(i->qp, destroyQp); + + // Set the qp context to this so we can find ourselves again + qp->qp_context = this; + } + + QueuePair::~QueuePair() { + if (outstandingSendEvents > 0) + ::ibv_ack_cq_events(scq.get(), outstandingSendEvents); + if (outstandingRecvEvents > 0) + ::ibv_ack_cq_events(rcq.get(), outstandingRecvEvents); + + // Reset back pointer in case someone else has the qp + qp->qp_context = 0; + } + + void QueuePair::postRecv(Buffer* buf) { + ::ibv_recv_wr rwr = {}; + ::ibv_sge sge; + + sge.addr = (uintptr_t) buf->bytes+buf->dataStart; + sge.length = buf->dataCount; + sge.lkey = buf->mr->lkey; + + rwr.wr_id = reinterpret_cast<uint64_t>(buf); + rwr.sg_list = &sge; + rwr.num_sge = 1; + + ::ibv_recv_wr* badrwr = 0; + CHECK_IBV(::ibv_post_recv(qp.get(), &rwr, &badrwr)); + if (badrwr) + throw std::logic_error("ibv_post_recv(): Bad rwr"); + } + + void QueuePair::postSend(Buffer* buf) { + ::ibv_send_wr swr = {}; + ::ibv_sge sge; + + sge.addr = (uintptr_t) buf->bytes+buf->dataStart; + sge.length = buf->dataCount; + sge.lkey = buf->mr->lkey; + + swr.wr_id = reinterpret_cast<uint64_t>(buf); + swr.opcode = IBV_WR_SEND; + swr.send_flags = IBV_SEND_SIGNALED; + swr.sg_list = &sge; + swr.num_sge = 1; + + ::ibv_send_wr* badswr = 0; + CHECK_IBV(::ibv_post_send(qp.get(), &swr, &badswr)); + if (badswr) + throw std::logic_error("ibv_post_send(): Bad swr"); + } + + void QueuePair::postSend(uint32_t imm, Buffer* buf) { + ::ibv_send_wr swr = {}; + ::ibv_sge sge; + + sge.addr = (uintptr_t) buf->bytes+buf->dataStart; + sge.length = buf->dataCount; + sge.lkey = buf->mr->lkey; + swr.send_flags = IBV_SEND_SIGNALED; + + swr.wr_id = reinterpret_cast<uint64_t>(buf); + swr.imm_data = htonl(imm); + swr.opcode = IBV_WR_SEND_WITH_IMM; + swr.sg_list = &sge; + swr.num_sge = 1; + + ::ibv_send_wr* badswr = 0; + CHECK_IBV(::ibv_post_send(qp.get(), &swr, &badswr)); + if (badswr) + throw std::logic_error("ibv_post_send(): Bad swr"); + } +} + +std::ostream& operator<<(std::ostream& o, ::rdma_cm_event_type t) { +# define CHECK_TYPE(t) case t: o << #t; break; + switch(t) { + CHECK_TYPE(RDMA_CM_EVENT_ADDR_RESOLVED) + CHECK_TYPE(RDMA_CM_EVENT_ADDR_ERROR) + CHECK_TYPE(RDMA_CM_EVENT_ROUTE_RESOLVED) + CHECK_TYPE(RDMA_CM_EVENT_ROUTE_ERROR) + CHECK_TYPE(RDMA_CM_EVENT_CONNECT_REQUEST) + CHECK_TYPE(RDMA_CM_EVENT_CONNECT_RESPONSE) + CHECK_TYPE(RDMA_CM_EVENT_CONNECT_ERROR) + CHECK_TYPE(RDMA_CM_EVENT_UNREACHABLE) + CHECK_TYPE(RDMA_CM_EVENT_REJECTED) + CHECK_TYPE(RDMA_CM_EVENT_ESTABLISHED) + CHECK_TYPE(RDMA_CM_EVENT_DISCONNECTED) + CHECK_TYPE(RDMA_CM_EVENT_DEVICE_REMOVAL) + CHECK_TYPE(RDMA_CM_EVENT_MULTICAST_JOIN) + CHECK_TYPE(RDMA_CM_EVENT_MULTICAST_ERROR) + } +# undef CHECK_TYPE + return o; +} diff --git a/RC5/cpp/src/qpid/sys/rdma/rdma_wrap.h b/RC5/cpp/src/qpid/sys/rdma/rdma_wrap.h new file mode 100644 index 0000000000..7812a02532 --- /dev/null +++ b/RC5/cpp/src/qpid/sys/rdma/rdma_wrap.h @@ -0,0 +1,498 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef RDMA_WRAP_H +#define RDMA_WRAP_H + +#include "rdma_factories.h" + +#include <rdma/rdma_cma.h> + +#include "qpid/RefCounted.h" +#include "qpid/sys/IOHandle.h" +#include "qpid/sys/posix/PrivatePosix.h" + +#include <fcntl.h> + +#include <netdb.h> + +#include <vector> +#include <algorithm> +#include <iostream> +#include <stdexcept> +#include <boost/shared_ptr.hpp> +#include <boost/intrusive_ptr.hpp> + +namespace Rdma { + const int DEFAULT_TIMEOUT = 2000; // 2 secs + const int DEFAULT_BACKLOG = 100; + const int DEFAULT_CQ_ENTRIES = 256; + const int DEFAULT_WR_ENTRIES = 64; + extern const ::rdma_conn_param DEFAULT_CONNECT_PARAM; + + int deviceCount(); + + struct Buffer { + friend class QueuePair; + + char* const bytes; + const int32_t byteCount; + int32_t dataStart; + int32_t dataCount; + + Buffer(::ibv_pd* pd, char* const b, const int32_t s) : + bytes(b), + byteCount(s), + dataStart(0), + dataCount(0), + mr(CHECK_NULL(::ibv_reg_mr( + pd, bytes, byteCount, + ::IBV_ACCESS_LOCAL_WRITE))) + {} + + ~Buffer() { + (void) ::ibv_dereg_mr(mr); + delete [] bytes; + } + + private: + ::ibv_mr* mr; + }; + + class Connection; + + enum QueueDirection { + NONE, + SEND, + RECV + }; + + class QueuePairEvent { + boost::shared_ptr< ::ibv_cq > cq; + ::ibv_wc wc; + QueueDirection dir; + + friend class QueuePair; + + QueuePairEvent() : + dir(NONE) + {} + + QueuePairEvent( + const ::ibv_wc& w, + boost::shared_ptr< ::ibv_cq > c, + QueueDirection d) : + cq(c), + wc(w), + dir(d) + { + assert(dir != NONE); + } + + public: + operator bool() const { + return dir != NONE; + } + + bool immPresent() const { + return wc.wc_flags & IBV_WC_WITH_IMM; + } + + uint32_t getImm() const { + return ntohl(wc.imm_data); + } + + QueueDirection getDirection() const { + return dir; + } + + ::ibv_wc_opcode getEventType() const { + return wc.opcode; + } + + ::ibv_wc_status getEventStatus() const { + return wc.status; + } + + Buffer* getBuffer() const { + Buffer* b = reinterpret_cast<Buffer*>(wc.wr_id); + b->dataCount = wc.byte_len; + return b; + } + }; + + // Wrapper for a queue pair - this has the functionality for + // putting buffers on the receive queue and for sending buffers + // to the other end of the connection. + class QueuePair : public qpid::sys::IOHandle, public qpid::RefCounted { + boost::shared_ptr< ::ibv_pd > pd; + boost::shared_ptr< ::ibv_comp_channel > cchannel; + boost::shared_ptr< ::ibv_cq > scq; + boost::shared_ptr< ::ibv_cq > rcq; + boost::shared_ptr< ::ibv_qp > qp; + int outstandingSendEvents; + int outstandingRecvEvents; + + friend class Connection; + + QueuePair(boost::shared_ptr< ::rdma_cm_id > id); + ~QueuePair(); + + public: + typedef boost::intrusive_ptr<QueuePair> intrusive_ptr; + + // Create a buffer to use for writing + Buffer* createBuffer(int s) { + return new Buffer(pd.get(), new char[s], s); + } + + // Make channel non-blocking by making + // associated fd nonblocking + void nonblocking() { + ::fcntl(cchannel->fd, F_SETFL, O_NONBLOCK); + } + + // If we get EAGAIN because the channel has been set non blocking + // and we'd have to wait then return an empty event + QueuePair::intrusive_ptr getNextChannelEvent() { + // First find out which cq has the event + ::ibv_cq* cq; + void* ctx; + int rc = ::ibv_get_cq_event(cchannel.get(), &cq, &ctx); + if (rc == -1 && errno == EAGAIN) + return 0; + CHECK(rc); + + // Batch acknowledge the event + if (cq == scq.get()) { + if (++outstandingSendEvents > DEFAULT_CQ_ENTRIES / 2) { + ::ibv_ack_cq_events(cq, outstandingSendEvents); + outstandingSendEvents = 0; + } + } else if (cq == rcq.get()) { + if (++outstandingRecvEvents > DEFAULT_CQ_ENTRIES / 2) { + ::ibv_ack_cq_events(cq, outstandingRecvEvents); + outstandingRecvEvents = 0; + } + } + + return static_cast<QueuePair*>(ctx); + } + + QueuePairEvent getNextEvent() { + ::ibv_wc w; + if (::ibv_poll_cq(scq.get(), 1, &w) == 1) + return QueuePairEvent(w, scq, SEND); + else if (::ibv_poll_cq(rcq.get(), 1, &w) == 1) + return QueuePairEvent(w, rcq, RECV); + else + return QueuePairEvent(); + } + + void postRecv(Buffer* buf); + void postSend(Buffer* buf); + void postSend(uint32_t imm, Buffer* buf); + void notifyRecv(); + void notifySend(); + }; + + class ConnectionEvent { + friend class Connection; + + // The order of the members is important as we have to acknowledge + // the event before destroying the ids on destruction + boost::intrusive_ptr<Connection> id; + boost::intrusive_ptr<Connection> listen_id; + boost::shared_ptr< ::rdma_cm_event > event; + + ConnectionEvent() {} + ConnectionEvent(::rdma_cm_event* e); + + // Default copy, assignment and destructor ok + public: + operator bool() const { + return event; + } + + ::rdma_cm_event_type getEventType() const { + return event->event; + } + + ::rdma_conn_param getConnectionParam() const; + + boost::intrusive_ptr<Connection> getConnection () const { + return id; + } + + boost::intrusive_ptr<Connection> getListenId() const { + return listen_id; + } + }; + + // For the moment this is a fairly simple wrapper for rdma_cm_id. + // + // NB: It allocates a protection domain (pd) per connection which means that + // registered buffers can't be shared between different connections + // (this can only happen between connections on the same controller in any case, + // so needs careful management if used) + class Connection : public qpid::sys::IOHandle, public qpid::RefCounted { + boost::shared_ptr< ::rdma_event_channel > channel; + boost::shared_ptr< ::rdma_cm_id > id; + QueuePair::intrusive_ptr qp; + + void* context; + + friend class ConnectionEvent; + friend class QueuePair; + + // Wrap the passed in rdma_cm_id with a Connection + // this basically happens only on connection request + Connection(::rdma_cm_id* i) : + qpid::sys::IOHandle(new qpid::sys::IOHandlePrivate), + id(i, destroyId), + context(0) + { + impl->fd = id->channel->fd; + + // Just overwrite the previous context as it will + // have come from the listening connection + if (i) + i->context = this; + } + + Connection() : + qpid::sys::IOHandle(new qpid::sys::IOHandlePrivate), + channel(mkEChannel()), + id(mkId(channel.get(), this, RDMA_PS_TCP)), + context(0) + { + impl->fd = channel->fd; + } + + ~Connection() { + // Reset the id context in case someone else has it + id->context = 0; + } + + // Default destructor fine + + void ensureQueuePair() { + assert(id.get()); + + // Only allocate a queue pair if there isn't one already + if (qp) + return; + + qp = new QueuePair(id); + } + + public: + typedef boost::intrusive_ptr<Connection> intrusive_ptr; + + static intrusive_ptr make() { + return new Connection(); + } + + static intrusive_ptr find(::rdma_cm_id* i) { + if (!i) + return 0; + Connection* id = static_cast< Connection* >(i->context); + if (!id) + throw std::logic_error("Couldn't find existing Connection"); + return id; + } + + template <typename T> + void addContext(T* c) { + // Don't allow replacing context + if (!context) + context = c; + } + + template <typename T> + T* getContext() { + return static_cast<T*>(context); + } + + // Make channel non-blocking by making + // associated fd nonblocking + void nonblocking() { + assert(id.get()); + ::fcntl(id->channel->fd, F_SETFL, O_NONBLOCK); + } + + // If we get EAGAIN because the channel has been set non blocking + // and we'd have to wait then return an empty event + ConnectionEvent getNextEvent() { + assert(id.get()); + ::rdma_cm_event* e; + int rc = ::rdma_get_cm_event(id->channel, &e); + if (rc == -1 && errno == EAGAIN) + return ConnectionEvent(); + CHECK(rc); + return ConnectionEvent(e); + } + + void bind(sockaddr& src_addr) const { + assert(id.get()); + CHECK(::rdma_bind_addr(id.get(), &src_addr)); + } + + void listen(int backlog = DEFAULT_BACKLOG) const { + assert(id.get()); + CHECK(::rdma_listen(id.get(), backlog)); + } + + void resolve_addr( + sockaddr& dst_addr, + sockaddr* src_addr = 0, + int timeout_ms = DEFAULT_TIMEOUT) const + { + assert(id.get()); + CHECK(::rdma_resolve_addr(id.get(), src_addr, &dst_addr, timeout_ms)); + } + + void resolve_route(int timeout_ms = DEFAULT_TIMEOUT) const { + assert(id.get()); + CHECK(::rdma_resolve_route(id.get(), timeout_ms)); + } + + void disconnect() const { + assert(id.get()); + CHECK(::rdma_disconnect(id.get())); + } + + // TODO: Currently you can only connect with the default connection parameters + void connect() { + assert(id.get()); + + // Need to have a queue pair before we can connect + ensureQueuePair(); + + ::rdma_conn_param p = DEFAULT_CONNECT_PARAM; + CHECK(::rdma_connect(id.get(), &p)); + } + + template <typename T> + void connect(const T* data) { + assert(id.get()); + // Need to have a queue pair before we can connect + ensureQueuePair(); + + ::rdma_conn_param p = DEFAULT_CONNECT_PARAM; + p.private_data = data; + p.private_data_len = sizeof(T); + CHECK(::rdma_connect(id.get(), &p)); + } + + // TODO: Not sure how to default accept params - they come from the connection request + // event + template <typename T> + void accept(const ::rdma_conn_param& param, const T* data) { + assert(id.get()); + // Need to have a queue pair before we can accept + ensureQueuePair(); + + ::rdma_conn_param p = param; + p.private_data = data; + p.private_data_len = sizeof(T); + CHECK(::rdma_accept(id.get(), &p)); + } + + void accept(const ::rdma_conn_param& param) { + assert(id.get()); + // Need to have a queue pair before we can accept + ensureQueuePair(); + + ::rdma_conn_param p = param; + p.private_data = 0; + p.private_data_len = 0; + CHECK(::rdma_accept(id.get(), &p)); + } + + template <typename T> + void reject(const T* data) const { + assert(id.get()); + CHECK(::rdma_reject(id.get(), data, sizeof(T))); + } + + void reject() const { + assert(id.get()); + CHECK(::rdma_reject(id.get(), 0, 0)); + } + + QueuePair::intrusive_ptr getQueuePair() { + assert(id.get()); + + ensureQueuePair(); + + return qp; + } + + std::string getLocalName() const { + ::sockaddr* addr = ::rdma_get_local_addr(id.get()); + char hostName[NI_MAXHOST]; + char portName[NI_MAXSERV]; + CHECK_IBV(::getnameinfo( + addr, sizeof(::sockaddr_storage), + hostName, sizeof(hostName), + portName, sizeof(portName), + NI_NUMERICHOST | NI_NUMERICSERV)); + std::string r(hostName); + r += ":"; + r += portName; + return r; + } + + std::string getPeerName() const { + ::sockaddr* addr = ::rdma_get_peer_addr(id.get()); + char hostName[NI_MAXHOST]; + char portName[NI_MAXSERV]; + CHECK_IBV(::getnameinfo( + addr, sizeof(::sockaddr_storage), + hostName, sizeof(hostName), + portName, sizeof(portName), + NI_NUMERICHOST | NI_NUMERICSERV)); + std::string r(hostName); + r += ":"; + r += portName; + return r; + } + }; + + inline void QueuePair::notifyRecv() { + CHECK_IBV(ibv_req_notify_cq(rcq.get(), 0)); + } + + inline void QueuePair::notifySend() { + CHECK_IBV(ibv_req_notify_cq(scq.get(), 0)); + } + + inline ConnectionEvent::ConnectionEvent(::rdma_cm_event* e) : + id((e->event != RDMA_CM_EVENT_CONNECT_REQUEST) ? + Connection::find(e->id) : new Connection(e->id)), + listen_id(Connection::find(e->listen_id)), + event(e, acker) + {} +} + +std::ostream& operator<<(std::ostream& o, ::rdma_cm_event_type t); + +#endif // RDMA_WRAP_H diff --git a/RC5/cpp/src/qpid/sys/solaris/ECFPoller.cpp b/RC5/cpp/src/qpid/sys/solaris/ECFPoller.cpp new file mode 100644 index 0000000000..783f84576b --- /dev/null +++ b/RC5/cpp/src/qpid/sys/solaris/ECFPoller.cpp @@ -0,0 +1,301 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/log/Logger.h" +#include "qpid/sys/Poller.h" +#include "qpid/sys/IOHandle.h" +#include "qpid/sys/Mutex.h" +#include "qpid/sys/DeletionManager.h" +#include "qpid/sys/posix/check.h" +#include "qpid/sys/posix/PrivatePosix.h" + +#include <port.h> +#include <poll.h> +#include <errno.h> + +#include <assert.h> +#include <vector> +#include <exception> + + +//TODO: Remove this +#include "qpid/sys/Dispatcher.h" + +namespace qpid { +namespace sys { + +// Deletion manager to handle deferring deletion of PollerHandles to when they definitely aren't being used +DeletionManager<PollerHandle> PollerHandleDeletionManager; + +// Instantiate (and define) class static for DeletionManager +template <> +DeletionManager<PollerHandle>::AllThreadsStatuses DeletionManager<PollerHandle>::allThreadsStatuses(0); + +class PollerHandlePrivate { + friend class Poller; + friend class PollerHandle; + + enum FDStat { + ABSENT, + MONITORED, + INACTIVE, + HUNGUP, + MONITORED_HUNGUP + }; + + int fd; + uint32_t events; + FDStat stat; + Mutex lock; + + PollerHandlePrivate(int f) : + fd(f), + events(0), + stat(ABSENT) { + } + + bool isActive() const { + return stat == MONITORED || stat == MONITORED_HUNGUP; + } + + void setActive() { + stat = (stat == HUNGUP) ? MONITORED_HUNGUP : MONITORED; + } + + bool isInactive() const { + return stat == INACTIVE || stat == HUNGUP; + } + + void setInactive() { + stat = INACTIVE; + } + + bool isIdle() const { + return stat == ABSENT; + } + + void setIdle() { + stat = ABSENT; + } + + bool isHungup() const { + return stat == MONITORED_HUNGUP || stat == HUNGUP; + } + + void setHungup() { + assert(stat == MONITORED); + stat = HUNGUP; + } +}; + +PollerHandle::PollerHandle(const IOHandle& h) : + impl(new PollerHandlePrivate(toFd(h.impl))) +{} + +PollerHandle::~PollerHandle() { + delete impl; +} + +void PollerHandle::deferDelete() { + PollerHandleDeletionManager.markForDeletion(this); +} + +/** + * Concrete implementation of Poller to use the Solaris Event Completion + * Framework interface + */ +class PollerPrivate { + friend class Poller; + + const int portId; + + static uint32_t directionToPollEvent(Poller::Direction dir) { + switch (dir) { + case Poller::INPUT: return POLLIN; + case Poller::OUTPUT: return POLLOUT; + case Poller::INOUT: return POLLIN | POLLOUT; + default: return 0; + } + } + + static Poller::EventType pollToDirection(uint32_t events) { + uint32_t e = events & (POLLIN | POLLOUT); + switch (e) { + case POLLIN: return Poller::READABLE; + case POLLOUT: return Poller::WRITABLE; + case POLLIN | POLLOUT: return Poller::READ_WRITABLE; + default: + return (events & (POLLHUP | POLLERR)) ? + Poller::DISCONNECTED : Poller::INVALID; + } + } + + PollerPrivate() : + portId(::port_create()) { + } + + ~PollerPrivate() { + } +}; + +void Poller::addFd(PollerHandle& handle, Direction dir) { + PollerHandlePrivate& eh = *handle.impl; + ScopedLock<Mutex> l(eh.lock); + + uint32_t events = 0; + + if (eh.isIdle()) { + events = PollerPrivate::directionToPollEvent(dir); + } else { + assert(eh.isActive()); + events = eh.events | PollerPrivate::directionToPollEvent(dir); + } + + //port_associate can be used to add an association or modify an + //existing one + QPID_POSIX_CHECK(::port_associate(impl->portId, PORT_SOURCE_FD, (uintptr_t) eh.fd, events, &handle)); + eh.events = events; + eh.setActive(); + QPID_LOG(trace, "Poller::addFd(handle=" << &handle + << "[" << typeid(&handle).name() + << "], fd=" << eh.fd << ")"); + //assert(dynamic_cast<DispatchHandle*>(&handle)); +} + +void Poller::delFd(PollerHandle& handle) { + PollerHandlePrivate& eh = *handle.impl; + ScopedLock<Mutex> l(eh.lock); + assert(!eh.isIdle()); + int rc = ::port_dissociate(impl->portId, PORT_SOURCE_FD, (uintptr_t) eh.fd); + //Allow closing an invalid fd, allowing users to close fd before + //doing delFd() + if (rc == -1 && errno != EBADFD) { + QPID_POSIX_CHECK(rc); + } + eh.setIdle(); + QPID_LOG(trace, "Poller::delFd(handle=" << &handle + << ", fd=" << eh.fd << ")"); +} + +// modFd is equivalent to delFd followed by addFd +void Poller::modFd(PollerHandle& handle, Direction dir) { + PollerHandlePrivate& eh = *handle.impl; + ScopedLock<Mutex> l(eh.lock); + assert(!eh.isIdle()); + + eh.events = PollerPrivate::directionToPollEvent(dir); + + //If fd is already associated, events and user arguments are updated + //So, no need to check if fd is already associated + QPID_POSIX_CHECK(::port_associate(impl->portId, PORT_SOURCE_FD, (uintptr_t) eh.fd, eh.events, &handle)); + eh.setActive(); + QPID_LOG(trace, "Poller::modFd(handle=" << &handle + << ", fd=" << eh.fd << ")"); +} + +void Poller::rearmFd(PollerHandle& handle) { + PollerHandlePrivate& eh = *handle.impl; + ScopedLock<Mutex> l(eh.lock); + assert(eh.isInactive()); + + QPID_POSIX_CHECK(::port_associate(impl->portId, PORT_SOURCE_FD, (uintptr_t) eh.fd, eh.events, &handle)); + eh.setActive(); + QPID_LOG(trace, "Poller::rearmdFd(handle=" << &handle + << ", fd=" << eh.fd << ")"); +} + +void Poller::shutdown() { + //Send an Alarm to the port + //We need to send a nonzero event mask, using POLLHUP, but + //The wait method will only look for a PORT_ALERT_SET + QPID_POSIX_CHECK(::port_alert(impl->portId, PORT_ALERT_SET, POLLHUP, NULL)); + QPID_LOG(trace, "Poller::shutdown"); +} + +Poller::Event Poller::wait(Duration timeout) { + timespec_t tout; + timespec_t* ptout = NULL; + port_event_t pe; + + if (timeout != TIME_INFINITE) { + tout.tv_sec = 0; + tout.tv_nsec = timeout; + ptout = &tout; + } + + do { + PollerHandleDeletionManager.markAllUnusedInThisThread(); + QPID_LOG(trace, "About to enter port_get. Thread " + << pthread_self() + << ", timeout=" << timeout); + + int rc = ::port_get(impl->portId, &pe, ptout); + + if (rc < 0) { + switch (errno) { + case EINTR: + continue; + case ETIME: + return Event(0, TIMEOUT); + default: + QPID_POSIX_CHECK(rc); + } + } else { + //We use alert mode to notify the shutdown of the Poller + if (pe.portev_source == PORT_SOURCE_ALERT) { + return Event(0, SHUTDOWN); + } + if (pe.portev_source == PORT_SOURCE_FD) { + PollerHandle *handle = static_cast<PollerHandle*>(pe.portev_user); + PollerHandlePrivate& eh = *handle->impl; + ScopedLock<Mutex> l(eh.lock); + QPID_LOG(trace, "About to send handle: " << handle); + + if (eh.isActive()) { + if (pe.portev_events & POLLHUP) { + if (eh.isHungup()) { + return Event(handle, DISCONNECTED); + } + eh.setHungup(); + } else { + eh.setInactive(); + } + QPID_LOG(trace, "Sending event (thread: " + << pthread_self() << ") for handle " << handle + << ", direction= " + << PollerPrivate::pollToDirection(pe.portev_events)); + return Event(handle, PollerPrivate::pollToDirection(pe.portev_events)); + } + } + } + } while (true); +} + +// Concrete constructors +Poller::Poller() : + impl(new PollerPrivate()) +{} + +Poller::~Poller() { + delete impl; +} + +}} diff --git a/RC5/cpp/src/qpid/sys/ssl/SslHandler.cpp b/RC5/cpp/src/qpid/sys/ssl/SslHandler.cpp new file mode 100644 index 0000000000..3c7e2190e7 --- /dev/null +++ b/RC5/cpp/src/qpid/sys/ssl/SslHandler.cpp @@ -0,0 +1,181 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "SslHandler.h" + +#include "SslIo.h" +#include "SslSocket.h" +#include "qpid/framing/AMQP_HighestVersion.h" +#include "qpid/framing/ProtocolInitiation.h" +#include "qpid/log/Statement.h" + +namespace qpid { +namespace sys { +namespace ssl { + + +// Buffer definition +struct Buff : public SslIO::BufferBase { + Buff() : + SslIO::BufferBase(new char[65536], 65536) + {} + ~Buff() + { delete [] bytes;} +}; + +SslHandler::SslHandler(std::string id, ConnectionCodec::Factory* f) : + identifier(id), + aio(0), + factory(f), + codec(0), + readError(false), + isClient(false) +{} + +SslHandler::~SslHandler() { + if (codec) + codec->closed(); + delete codec; +} + +void SslHandler::init(SslIO* a, int numBuffs) { + aio = a; + + // Give connection some buffers to use + for (int i = 0; i < numBuffs; i++) { + aio->queueReadBuffer(new Buff); + } +} + +void SslHandler::write(const framing::ProtocolInitiation& data) +{ + QPID_LOG(debug, "SENT [" << identifier << "] INIT(" << data << ")"); + SslIO::BufferBase* buff = aio->getQueuedBuffer(); + if (!buff) + buff = new Buff; + framing::Buffer out(buff->bytes, buff->byteCount); + data.encode(out); + buff->dataCount = data.encodedSize(); + aio->queueWrite(buff); +} + +void SslHandler::activateOutput() { + aio->notifyPendingWrite(); +} + +void SslHandler::giveReadCredit(int32_t) { + // FIXME aconway 2008-12-05: not yet implemented. +} + +// Input side +void SslHandler::readbuff(SslIO& , SslIO::BufferBase* buff) { + if (readError) { + return; + } + size_t decoded = 0; + if (codec) { // Already initiated + try { + decoded = codec->decode(buff->bytes+buff->dataStart, buff->dataCount); + }catch(const std::exception& e){ + QPID_LOG(error, e.what()); + readError = true; + aio->queueWriteClose(); + } + }else{ + framing::Buffer in(buff->bytes+buff->dataStart, buff->dataCount); + framing::ProtocolInitiation protocolInit; + if (protocolInit.decode(in)) { + decoded = in.getPosition(); + QPID_LOG(debug, "RECV [" << identifier << "] INIT(" << protocolInit << ")"); + try { + codec = factory->create(protocolInit.getVersion(), *this, identifier); + if (!codec) { + //TODO: may still want to revise this... + //send valid version header & close connection. + write(framing::ProtocolInitiation(framing::highestProtocolVersion)); + readError = true; + aio->queueWriteClose(); + } + } catch (const std::exception& e) { + QPID_LOG(error, e.what()); + readError = true; + aio->queueWriteClose(); + } + } + } + // TODO: unreading needs to go away, and when we can cope + // with multiple sub-buffers in the general buffer scheme, it will + if (decoded != size_t(buff->dataCount)) { + // Adjust buffer for used bytes and then "unread them" + buff->dataStart += decoded; + buff->dataCount -= decoded; + aio->unread(buff); + } else { + // Give whole buffer back to aio subsystem + aio->queueReadBuffer(buff); + } +} + +void SslHandler::eof(SslIO&) { + QPID_LOG(debug, "DISCONNECTED [" << identifier << "]"); + if (codec) codec->closed(); + aio->queueWriteClose(); +} + +void SslHandler::closedSocket(SslIO&, const SslSocket& s) { + // If we closed with data still to send log a warning + if (!aio->writeQueueEmpty()) { + QPID_LOG(warning, "CLOSING [" << identifier << "] unsent data (probably due to client disconnect)"); + } + delete &s; + aio->queueForDeletion(); + delete this; +} + +void SslHandler::disconnect(SslIO& a) { + // treat the same as eof + eof(a); +} + +// Notifications +void SslHandler::nobuffs(SslIO&) { +} + +void SslHandler::idle(SslIO&){ + if (isClient && codec == 0) { + codec = factory->create(*this, identifier); + write(framing::ProtocolInitiation(codec->getVersion())); + return; + } + if (codec == 0) return; + if (codec->canEncode()) { + // Try and get a queued buffer if not then construct new one + SslIO::BufferBase* buff = aio->getQueuedBuffer(); + if (!buff) buff = new Buff; + size_t encoded=codec->encode(buff->bytes, buff->byteCount); + buff->dataCount = encoded; + aio->queueWrite(buff); + } + if (codec->isClosed()) + aio->queueWriteClose(); +} + + +}}} // namespace qpid::sys::ssl diff --git a/RC5/cpp/src/qpid/sys/ssl/SslHandler.h b/RC5/cpp/src/qpid/sys/ssl/SslHandler.h new file mode 100644 index 0000000000..ae654d7ad2 --- /dev/null +++ b/RC5/cpp/src/qpid/sys/ssl/SslHandler.h @@ -0,0 +1,76 @@ +#ifndef QPID_SYS_SSL_SSLHANDLER_H +#define QPID_SYS_SSL_SSLHANDLER_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/sys/ConnectionCodec.h" +#include "qpid/sys/OutputControl.h" + +namespace qpid { + +namespace framing { + class ProtocolInitiation; +} + +namespace sys { +namespace ssl { + +class SslIO; +class SslIOBufferBase; +class SslSocket; + +class SslHandler : public OutputControl { + std::string identifier; + SslIO* aio; + ConnectionCodec::Factory* factory; + ConnectionCodec* codec; + bool readError; + bool isClient; + + void write(const framing::ProtocolInitiation&); + + public: + SslHandler(std::string id, ConnectionCodec::Factory* f); + ~SslHandler(); + void init(SslIO* a, int numBuffs); + + void setClient() { isClient = true; } + + // Output side + void close(); + void activateOutput(); + void giveReadCredit(int32_t); + + // Input side + void readbuff(SslIO& aio, SslIOBufferBase* buff); + void eof(SslIO& aio); + void disconnect(SslIO& aio); + + // Notifications + void nobuffs(SslIO& aio); + void idle(SslIO& aio); + void closedSocket(SslIO& aio, const SslSocket& s); +}; + +}}} // namespace qpid::sys::ssl + +#endif /*!QPID_SYS_SSL_SSLHANDLER_H*/ diff --git a/RC5/cpp/src/qpid/sys/ssl/SslIo.cpp b/RC5/cpp/src/qpid/sys/ssl/SslIo.cpp new file mode 100644 index 0000000000..9be75af47d --- /dev/null +++ b/RC5/cpp/src/qpid/sys/ssl/SslIo.cpp @@ -0,0 +1,433 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "SslIo.h" +#include "SslSocket.h" + +#include "qpid/sys/Time.h" +#include "qpid/sys/posix/check.h" +#include "qpid/log/Statement.h" + +// TODO The basic algorithm here is not really POSIX specific and with a bit more abstraction +// could (should) be promoted to be platform portable +#include <unistd.h> +#include <sys/socket.h> +#include <signal.h> +#include <errno.h> +#include <string.h> + +#include <boost/bind.hpp> + +using namespace qpid::sys; +using namespace qpid::sys::ssl; + +namespace { + +/* + * Make *process* not generate SIGPIPE when writing to closed + * pipe/socket (necessary as default action is to terminate process) + */ +void ignoreSigpipe() { + ::signal(SIGPIPE, SIG_IGN); +} + +/* + * We keep per thread state to avoid locking overhead. The assumption is that + * on average all the connections are serviced by all the threads so the state + * recorded in each thread is about the same. If this turns out not to be the + * case we could rebalance the info occasionally. + */ +__thread int threadReadTotal = 0; +__thread int threadMaxRead = 0; +__thread int threadReadCount = 0; +__thread int threadWriteTotal = 0; +__thread int threadWriteCount = 0; +__thread int64_t threadMaxReadTimeNs = 2 * 1000000; // start at 2ms +} + +/* + * Asynch Acceptor + */ + +SslAcceptor::SslAcceptor(const SslSocket& s, Callback callback) : + acceptedCallback(callback), + handle(s, boost::bind(&SslAcceptor::readable, this, _1), 0, 0), + socket(s) { + + s.setNonblocking(); + ignoreSigpipe(); +} + +void SslAcceptor::start(Poller::shared_ptr poller) { + handle.startWatch(poller); +} + +/* + * We keep on accepting as long as there is something to accept + */ +void SslAcceptor::readable(DispatchHandle& h) { + SslSocket* s; + do { + errno = 0; + // TODO: Currently we ignore the peers address, perhaps we should + // log it or use it for connection acceptance. + try { + s = socket.accept(0, 0); + if (s) { + acceptedCallback(*s); + } else { + break; + } + } catch (const std::exception& e) { + QPID_LOG(error, "Could not accept socket: " << e.what()); + } + } while (true); + + h.rewatch(); +} + +/* + * Asynch Connector + */ + +SslConnector::SslConnector(const SslSocket& s, + Poller::shared_ptr poller, + std::string hostname, + uint16_t port, + ConnectedCallback connCb, + FailedCallback failCb) : + DispatchHandle(s, + 0, + boost::bind(&SslConnector::connComplete, this, _1), + boost::bind(&SslConnector::connComplete, this, _1)), + connCallback(connCb), + failCallback(failCb), + socket(s) +{ + //TODO: would be better for connect to be performed on a + //non-blocking socket, but that doesn't work at present so connect + //blocks until complete + try { + socket.connect(hostname, port); + socket.setNonblocking(); + startWatch(poller); + } catch(std::exception& e) { + failure(-1, std::string(e.what())); + } +} + +void SslConnector::connComplete(DispatchHandle& h) +{ + int errCode = socket.getError(); + + h.stopWatch(); + if (errCode == 0) { + connCallback(socket); + DispatchHandle::doDelete(); + } else { + // TODO: This need to be fixed as strerror isn't thread safe + failure(errCode, std::string(::strerror(errCode))); + } +} + +void SslConnector::failure(int errCode, std::string message) +{ + if (failCallback) + failCallback(errCode, message); + + socket.close(); + delete &socket; + + DispatchHandle::doDelete(); +} + +/* + * Asynch reader/writer + */ +SslIO::SslIO(const SslSocket& s, + ReadCallback rCb, EofCallback eofCb, DisconnectCallback disCb, + ClosedCallback cCb, BuffersEmptyCallback eCb, IdleCallback iCb) : + + DispatchHandle(s, + boost::bind(&SslIO::readable, this, _1), + boost::bind(&SslIO::writeable, this, _1), + boost::bind(&SslIO::disconnected, this, _1)), + readCallback(rCb), + eofCallback(eofCb), + disCallback(disCb), + closedCallback(cCb), + emptyCallback(eCb), + idleCallback(iCb), + socket(s), + queuedClose(false), + writePending(false) { + + s.setNonblocking(); +} + +struct deleter +{ + template <typename T> + void operator()(T *ptr){ delete ptr;} +}; + +SslIO::~SslIO() { + std::for_each( bufferQueue.begin(), bufferQueue.end(), deleter()); + std::for_each( writeQueue.begin(), writeQueue.end(), deleter()); +} + +void SslIO::queueForDeletion() { + DispatchHandle::doDelete(); +} + +void SslIO::start(Poller::shared_ptr poller) { + DispatchHandle::startWatch(poller); +} + +void SslIO::queueReadBuffer(BufferBase* buff) { + assert(buff); + buff->dataStart = 0; + buff->dataCount = 0; + bufferQueue.push_back(buff); + DispatchHandle::rewatchRead(); +} + +void SslIO::unread(BufferBase* buff) { + assert(buff); + if (buff->dataStart != 0) { + memmove(buff->bytes, buff->bytes+buff->dataStart, buff->dataCount); + buff->dataStart = 0; + } + bufferQueue.push_front(buff); + DispatchHandle::rewatchRead(); +} + +void SslIO::queueWrite(BufferBase* buff) { + assert(buff); + // If we've already closed the socket then throw the write away + if (queuedClose) { + bufferQueue.push_front(buff); + return; + } else { + writeQueue.push_front(buff); + } + writePending = false; + DispatchHandle::rewatchWrite(); +} + +void SslIO::notifyPendingWrite() { + writePending = true; + DispatchHandle::rewatchWrite(); +} + +void SslIO::queueWriteClose() { + queuedClose = true; + DispatchHandle::rewatchWrite(); +} + +/** Return a queued buffer if there are enough + * to spare + */ +SslIO::BufferBase* SslIO::getQueuedBuffer() { + // Always keep at least one buffer (it might have data that was "unread" in it) + if (bufferQueue.size()<=1) + return 0; + BufferBase* buff = bufferQueue.back(); + assert(buff); + buff->dataStart = 0; + buff->dataCount = 0; + bufferQueue.pop_back(); + return buff; +} + +/* + * We keep on reading as long as we have something to read and a buffer to put + * it in + */ +void SslIO::readable(DispatchHandle& h) { + int readTotal = 0; + AbsTime readStartTime = AbsTime::now(); + do { + // (Try to) get a buffer + if (!bufferQueue.empty()) { + // Read into buffer + BufferBase* buff = bufferQueue.front(); + assert(buff); + bufferQueue.pop_front(); + errno = 0; + int readCount = buff->byteCount-buff->dataCount; + int rc = socket.read(buff->bytes + buff->dataCount, readCount); + if (rc > 0) { + buff->dataCount += rc; + threadReadTotal += rc; + readTotal += rc; + + readCallback(*this, buff); + if (rc != readCount) { + // If we didn't fill the read buffer then time to stop reading + break; + } + + // Stop reading if we've overrun our timeslot + if (Duration(readStartTime, AbsTime::now()) > threadMaxReadTimeNs) { + break; + } + + } else { + // Put buffer back (at front so it doesn't interfere with unread buffers) + bufferQueue.push_front(buff); + assert(buff); + + // Eof or other side has gone away + if (rc == 0 || errno == ECONNRESET) { + eofCallback(*this); + h.unwatchRead(); + break; + } else if (errno == EAGAIN) { + // We have just put a buffer back so we know + // we can carry on watching for reads + break; + } else { + // Report error then just treat as a socket disconnect + QPID_LOG(error, "Error reading socket: " << qpid::sys::strError(rc) << "(" << rc << ")" ); + eofCallback(*this); + h.unwatchRead(); + break; + } + } + } else { + // Something to read but no buffer + if (emptyCallback) { + emptyCallback(*this); + } + // If we still have no buffers we can't do anything more + if (bufferQueue.empty()) { + h.unwatchRead(); + break; + } + + } + } while (true); + + ++threadReadCount; + threadMaxRead = std::max(threadMaxRead, readTotal); + return; +} + +/* + * We carry on writing whilst we have data to write and we can write + */ +void SslIO::writeable(DispatchHandle& h) { + int writeTotal = 0; + do { + // See if we've got something to write + if (!writeQueue.empty()) { + // Write buffer + BufferBase* buff = writeQueue.back(); + writeQueue.pop_back(); + errno = 0; + assert(buff->dataStart+buff->dataCount <= buff->byteCount); + int rc = socket.write(buff->bytes+buff->dataStart, buff->dataCount); + if (rc >= 0) { + threadWriteTotal += rc; + writeTotal += rc; + + // If we didn't write full buffer put rest back + if (rc != buff->dataCount) { + buff->dataStart += rc; + buff->dataCount -= rc; + writeQueue.push_back(buff); + break; + } + + // Recycle the buffer + queueReadBuffer(buff); + + // If we've already written more than the max for reading then stop + // (this is to stop writes dominating reads) + if (writeTotal > threadMaxRead) + break; + } else { + // Put buffer back + writeQueue.push_back(buff); + if (errno == ECONNRESET || errno == EPIPE) { + // Just stop watching for write here - we'll get a + // disconnect callback soon enough + h.unwatchWrite(); + break; + } else if (errno == EAGAIN) { + // We have just put a buffer back so we know + // we can carry on watching for writes + break; + } else { + QPID_POSIX_CHECK(rc); + } + } + } else { + // If we're waiting to close the socket then can do it now as there is nothing to write + if (queuedClose) { + close(h); + break; + } + // Fd is writable, but nothing to write + if (idleCallback) { + writePending = false; + idleCallback(*this); + } + // If we still have no buffers to write we can't do anything more + if (writeQueue.empty() && !writePending && !queuedClose) { + h.unwatchWrite(); + // The following handles the case where writePending is + // set to true after the test above; in this case its + // possible that the unwatchWrite overwrites the + // desired rewatchWrite so we correct that here + if (writePending) + h.rewatchWrite(); + break; + } + } + } while (true); + + ++threadWriteCount; + return; +} + +void SslIO::disconnected(DispatchHandle& h) { + // If we've already queued close do it instead of disconnected callback + if (queuedClose) { + close(h); + } else if (disCallback) { + disCallback(*this); + h.unwatch(); + } +} + +/* + * Close the socket and callback to say we've done it + */ +void SslIO::close(DispatchHandle& h) { + h.stopWatch(); + socket.close(); + if (closedCallback) { + closedCallback(*this, socket); + } +} + diff --git a/RC5/cpp/src/qpid/sys/ssl/SslIo.h b/RC5/cpp/src/qpid/sys/ssl/SslIo.h new file mode 100644 index 0000000000..ba6483282b --- /dev/null +++ b/RC5/cpp/src/qpid/sys/ssl/SslIo.h @@ -0,0 +1,167 @@ +#ifndef _sys_ssl_SslIO +#define _sys_ssl_SslIO +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/sys/DispatchHandle.h" + +#include <boost/function.hpp> +#include <deque> + +namespace qpid { +namespace sys { +namespace ssl { + +class SslSocket; + +/* + * Asynchronous ssl acceptor: accepts connections then does a callback + * with the accepted fd + */ +class SslAcceptor { +public: + typedef boost::function1<void, const SslSocket&> Callback; + +private: + Callback acceptedCallback; + qpid::sys::DispatchHandle handle; + const SslSocket& socket; + +public: + SslAcceptor(const SslSocket& s, Callback callback); + void start(qpid::sys::Poller::shared_ptr poller); + +private: + void readable(qpid::sys::DispatchHandle& handle); +}; + +/* + * Asynchronous ssl connector: starts the process of initiating a + * connection and invokes a callback when completed or failed. + */ +class SslConnector : private qpid::sys::DispatchHandle { +public: + typedef boost::function1<void, const SslSocket&> ConnectedCallback; + typedef boost::function2<void, int, std::string> FailedCallback; + +private: + ConnectedCallback connCallback; + FailedCallback failCallback; + const SslSocket& socket; + +public: + SslConnector(const SslSocket& socket, + Poller::shared_ptr poller, + std::string hostname, + uint16_t port, + ConnectedCallback connCb, + FailedCallback failCb = 0); + +private: + void connComplete(DispatchHandle& handle); + void failure(int, std::string); +}; + +struct SslIOBufferBase { + char* const bytes; + const int32_t byteCount; + int32_t dataStart; + int32_t dataCount; + + SslIOBufferBase(char* const b, const int32_t s) : + bytes(b), + byteCount(s), + dataStart(0), + dataCount(0) + {} + + virtual ~SslIOBufferBase() + {} +}; + +/* + * Asychronous reader/writer: + * Reader accepts buffers to read into; reads into the provided buffers + * and then does a callback with the buffer and amount read. Optionally it can callback + * when there is something to read but no buffer to read it into. + * + * Writer accepts a buffer and queues it for writing; can also be given + * a callback for when writing is "idle" (ie fd is writable, but nothing to write) + * + * The class is implemented in terms of DispatchHandle to allow it to be deleted by deleting + * the contained DispatchHandle + */ +class SslIO : private qpid::sys::DispatchHandle { +public: + typedef SslIOBufferBase BufferBase; + + typedef boost::function2<void, SslIO&, BufferBase*> ReadCallback; + typedef boost::function1<void, SslIO&> EofCallback; + typedef boost::function1<void, SslIO&> DisconnectCallback; + typedef boost::function2<void, SslIO&, const SslSocket&> ClosedCallback; + typedef boost::function1<void, SslIO&> BuffersEmptyCallback; + typedef boost::function1<void, SslIO&> IdleCallback; + +private: + ReadCallback readCallback; + EofCallback eofCallback; + DisconnectCallback disCallback; + ClosedCallback closedCallback; + BuffersEmptyCallback emptyCallback; + IdleCallback idleCallback; + const SslSocket& socket; + std::deque<BufferBase*> bufferQueue; + std::deque<BufferBase*> writeQueue; + bool queuedClose; + /** + * This flag is used to detect and handle concurrency between + * calls to notifyPendingWrite() (which can be made from any thread) and + * the execution of the writeable() method (which is always on the + * thread processing this handle. + */ + volatile bool writePending; + +public: + SslIO(const SslSocket& s, + ReadCallback rCb, EofCallback eofCb, DisconnectCallback disCb, + ClosedCallback cCb = 0, BuffersEmptyCallback eCb = 0, IdleCallback iCb = 0); + void queueForDeletion(); + + void start(qpid::sys::Poller::shared_ptr poller); + void queueReadBuffer(BufferBase* buff); + void unread(BufferBase* buff); + void queueWrite(BufferBase* buff); + void notifyPendingWrite(); + void queueWriteClose(); + bool writeQueueEmpty() { return writeQueue.empty(); } + BufferBase* getQueuedBuffer(); + +private: + ~SslIO(); + void readable(qpid::sys::DispatchHandle& handle); + void writeable(qpid::sys::DispatchHandle& handle); + void disconnected(qpid::sys::DispatchHandle& handle); + void close(qpid::sys::DispatchHandle& handle); +}; + +}}} + +#endif // _sys_ssl_SslIO diff --git a/RC5/cpp/src/qpid/sys/ssl/SslSocket.cpp b/RC5/cpp/src/qpid/sys/ssl/SslSocket.cpp new file mode 100644 index 0000000000..597fbe57db --- /dev/null +++ b/RC5/cpp/src/qpid/sys/ssl/SslSocket.cpp @@ -0,0 +1,279 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "SslSocket.h" +#include "check.h" +#include "util.h" +#include "qpid/Exception.h" +#include "qpid/sys/posix/check.h" +#include "qpid/sys/posix/PrivatePosix.h" + +#include <fcntl.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/errno.h> +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <netdb.h> +#include <cstdlib> +#include <string.h> +#include <iostream> + +#include <nspr4/private/pprio.h> +#include <nss3/nss.h> +#include <nss3/pk11pub.h> +#include <nss3/ssl.h> +#include <nss3/key.h> + +#include <boost/format.hpp> + +namespace qpid { +namespace sys { +namespace ssl { + +namespace { +std::string getName(int fd, bool local, bool includeService = false) +{ + ::sockaddr_storage name; // big enough for any socket address + ::socklen_t namelen = sizeof(name); + + int result = -1; + if (local) { + result = ::getsockname(fd, (::sockaddr*)&name, &namelen); + } else { + result = ::getpeername(fd, (::sockaddr*)&name, &namelen); + } + + QPID_POSIX_CHECK(result); + + char servName[NI_MAXSERV]; + char dispName[NI_MAXHOST]; + if (includeService) { + if (int rc=::getnameinfo((::sockaddr*)&name, namelen, dispName, sizeof(dispName), + servName, sizeof(servName), + NI_NUMERICHOST | NI_NUMERICSERV) != 0) + throw QPID_POSIX_ERROR(rc); + return std::string(dispName) + ":" + std::string(servName); + + } else { + if (int rc=::getnameinfo((::sockaddr*)&name, namelen, dispName, sizeof(dispName), 0, 0, NI_NUMERICHOST) != 0) + throw QPID_POSIX_ERROR(rc); + return dispName; + } +} + +std::string getService(int fd, bool local) +{ + ::sockaddr_storage name; // big enough for any socket address + ::socklen_t namelen = sizeof(name); + + int result = -1; + if (local) { + result = ::getsockname(fd, (::sockaddr*)&name, &namelen); + } else { + result = ::getpeername(fd, (::sockaddr*)&name, &namelen); + } + + QPID_POSIX_CHECK(result); + + char servName[NI_MAXSERV]; + if (int rc=::getnameinfo((::sockaddr*)&name, namelen, 0, 0, + servName, sizeof(servName), + NI_NUMERICHOST | NI_NUMERICSERV) != 0) + throw QPID_POSIX_ERROR(rc); + return servName; +} + +} + +SslSocket::SslSocket() : IOHandle(new IOHandlePrivate()), socket(0), prototype(0) +{ + impl->fd = ::socket (PF_INET, SOCK_STREAM, 0); + if (impl->fd < 0) throw QPID_POSIX_ERROR(errno); + socket = SSL_ImportFD(0, PR_ImportTCPSocket(impl->fd)); +} + +/** + * This form of the constructor is used with the server-side sockets + * returned from accept. Because we use posix accept rather than + * PR_Accept, we have to reset the handshake. + */ +SslSocket::SslSocket(IOHandlePrivate* ioph, PRFileDesc* model) : IOHandle(ioph), socket(0), prototype(0) +{ + socket = SSL_ImportFD(model, PR_ImportTCPSocket(impl->fd)); + NSS_CHECK(SSL_ResetHandshake(socket, true)); + NSS_CHECK(SSL_ForceHandshake(socket)); +} + +void SslSocket::setNonblocking() const +{ + PRSocketOptionData option; + option.option = PR_SockOpt_Nonblocking; + option.value.non_blocking = true; + PR_SetSocketOption(socket, &option); +} + +void SslSocket::connect(const std::string& host, uint16_t port) const +{ + std::stringstream namestream; + namestream << host << ":" << port; + connectname = namestream.str(); + + void* arg = SslOptions::global.certName.empty() ? 0 : const_cast<char*>(SslOptions::global.certName.c_str()); + NSS_CHECK(SSL_GetClientAuthDataHook(socket, NSS_GetClientAuthData, arg)); + NSS_CHECK(SSL_SetURL(socket, host.data())); + + char hostBuffer[PR_NETDB_BUF_SIZE]; + PRHostEnt hostEntry; + PR_CHECK(PR_GetHostByName(host.data(), hostBuffer, PR_NETDB_BUF_SIZE, &hostEntry)); + PRNetAddr address; + int value = PR_EnumerateHostEnt(0, &hostEntry, port, &address); + if (value < 0) { + throw Exception(QPID_MSG("Error getting address for host: " << ErrorString())); + } else if (value == 0) { + throw Exception(QPID_MSG("Could not resolve address for host.")); + } + PR_CHECK(PR_Connect(socket, &address, PR_INTERVAL_NO_TIMEOUT)); + NSS_CHECK(SSL_ForceHandshake(socket)); +} + +void SslSocket::close() const +{ + if (impl->fd > 0) { + PR_Close(socket); + impl->fd = -1; + } +} + +int SslSocket::listen(uint16_t port, int backlog, const std::string& certName, bool clientAuth) const +{ + //configure prototype socket: + prototype = SSL_ImportFD(0, PR_NewTCPSocket()); + if (clientAuth) { + NSS_CHECK(SSL_OptionSet(prototype, SSL_REQUEST_CERTIFICATE, PR_TRUE)); + NSS_CHECK(SSL_OptionSet(prototype, SSL_REQUIRE_CERTIFICATE, PR_TRUE)); + } + + //get certificate and key (is this the correct way?) + CERTCertificate *cert = PK11_FindCertFromNickname(const_cast<char*>(certName.c_str()), 0); + if (!cert) throw Exception(QPID_MSG("Failed to load certificate '" << certName << "'")); + SECKEYPrivateKey *key = PK11_FindKeyByAnyCert(cert, 0); + if (!key) throw Exception(QPID_MSG("Failed to retrieve private key from certificate")); + NSS_CHECK(SSL_ConfigSecureServer(prototype, cert, key, NSS_FindCertKEAType(cert))); + SECKEY_DestroyPrivateKey(key); + CERT_DestroyCertificate(cert); + + //bind and listen + const int& socket = impl->fd; + int yes=1; + QPID_POSIX_CHECK(setsockopt(socket,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(yes))); + struct sockaddr_in name; + name.sin_family = AF_INET; + name.sin_port = htons(port); + name.sin_addr.s_addr = 0; + if (::bind(socket, (struct sockaddr*)&name, sizeof(name)) < 0) + throw Exception(QPID_MSG("Can't bind to port " << port << ": " << strError(errno))); + if (::listen(socket, backlog) < 0) + throw Exception(QPID_MSG("Can't listen on port " << port << ": " << strError(errno))); + + socklen_t namelen = sizeof(name); + if (::getsockname(socket, (struct sockaddr*)&name, &namelen) < 0) + throw QPID_POSIX_ERROR(errno); + + return ntohs(name.sin_port); +} + +SslSocket* SslSocket::accept(struct sockaddr *addr, socklen_t *addrlen) const +{ + int afd = ::accept(impl->fd, addr, addrlen); + if ( afd >= 0) { + return new SslSocket(new IOHandlePrivate(afd), prototype); + } else if (errno == EAGAIN) { + return 0; + } else { + throw QPID_POSIX_ERROR(errno); + } +} + +int SslSocket::read(void *buf, size_t count) const +{ + return PR_Read(socket, buf, count); +} + +int SslSocket::write(const void *buf, size_t count) const +{ + return PR_Write(socket, buf, count); +} + +std::string SslSocket::getSockname() const +{ + return getName(impl->fd, true); +} + +std::string SslSocket::getPeername() const +{ + return getName(impl->fd, false); +} + +std::string SslSocket::getPeerAddress() const +{ + if (!connectname.empty()) + return connectname; + return getName(impl->fd, false, true); +} + +std::string SslSocket::getLocalAddress() const +{ + return getName(impl->fd, true, true); +} + +uint16_t SslSocket::getLocalPort() const +{ + return std::atoi(getService(impl->fd, true).c_str()); +} + +uint16_t SslSocket::getRemotePort() const +{ + return atoi(getService(impl->fd, true).c_str()); +} + +int SslSocket::getError() const +{ + int result; + socklen_t rSize = sizeof (result); + + if (::getsockopt(impl->fd, SOL_SOCKET, SO_ERROR, &result, &rSize) < 0) + throw QPID_POSIX_ERROR(errno); + + return result; +} + +void SslSocket::setTcpNoDelay(bool nodelay) const +{ + if (nodelay) { + PRSocketOptionData option; + option.option = PR_SockOpt_NoDelay; + option.value.no_delay = true; + PR_SetSocketOption(socket, &option); + } +} + +}}} // namespace qpid::sys::ssl diff --git a/RC5/cpp/src/qpid/sys/ssl/SslSocket.h b/RC5/cpp/src/qpid/sys/ssl/SslSocket.h new file mode 100644 index 0000000000..a82e9133e8 --- /dev/null +++ b/RC5/cpp/src/qpid/sys/ssl/SslSocket.h @@ -0,0 +1,117 @@ +#ifndef _sys_ssl_Socket_h +#define _sys_ssl_Socket_h + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/sys/IOHandle.h" +#include <nspr4/nspr.h> + +#include <string> + +struct sockaddr; + +namespace qpid { +namespace sys { + +class Duration; + +namespace ssl { + +class SslSocket : public qpid::sys::IOHandle +{ +public: + /** Create a socket wrapper for descriptor. */ + SslSocket(); + + /** Set socket non blocking */ + void setNonblocking() const; + + /** Set tcp-nodelay */ + void setTcpNoDelay(bool nodelay) const; + + void connect(const std::string& host, uint16_t port) const; + + void close() const; + + /** Bind to a port and start listening. + *@param port 0 means choose an available port. + *@param backlog maximum number of pending connections. + *@param certName name of certificate to use to identify the server + *@return The bound port. + */ + int listen(uint16_t port = 0, int backlog = 10, const std::string& certName = "localhost.localdomain", bool clientAuth = false) const; + + /** + * Accept a connection from a socket that is already listening + * and has an incoming connection + */ + SslSocket* accept(struct sockaddr *addr, socklen_t *addrlen) const; + + // TODO The following are raw operations, maybe they need better wrapping? + int read(void *buf, size_t count) const; + int write(const void *buf, size_t count) const; + + /** Returns the "socket name" ie the address bound to + * the near end of the socket + */ + std::string getSockname() const; + + /** Returns the "peer name" ie the address bound to + * the remote end of the socket + */ + std::string getPeername() const; + + /** + * Returns an address (host and port) for the remote end of the + * socket + */ + std::string getPeerAddress() const; + /** + * Returns an address (host and port) for the local end of the + * socket + */ + std::string getLocalAddress() const; + + uint16_t getLocalPort() const; + uint16_t getRemotePort() const; + + /** + * Returns the error code stored in the socket. This may be used + * to determine the result of a non-blocking connect. + */ + int getError() const; + +private: + mutable std::string connectname; + mutable PRFileDesc* socket; + /** + * 'model' socket, with configuration to use when importing + * accepted sockets for use as ssl sockets. Set on listen(), used + * in accept to pass through to newly created socket instances. + */ + mutable PRFileDesc* prototype; + + SslSocket(IOHandlePrivate* ioph, PRFileDesc* model); +}; + +}}} +#endif /*!_sys_ssl_Socket_h*/ diff --git a/RC5/cpp/src/qpid/sys/ssl/check.cpp b/RC5/cpp/src/qpid/sys/ssl/check.cpp new file mode 100644 index 0000000000..b580e9bcf5 --- /dev/null +++ b/RC5/cpp/src/qpid/sys/ssl/check.cpp @@ -0,0 +1,72 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "check.h" +#include <nss3/secerr.h> +#include <nss3/sslerr.h> +#include <boost/format.hpp> + +using boost::format; +using boost::str; + +namespace qpid { +namespace sys { +namespace ssl { + +const std::string SSL_ERROR_BAD_CERT_DOMAIN_STR = + "Unable to communicate securely with peer: requested domain name does not match the server's certificate."; +const std::string SSL_ERROR_BAD_CERT_ALERT_STR = "SSL peer cannot verify your certificate."; +const std::string SEC_ERROR_BAD_DATABASE_STR = "Security library: bad database."; +const std::string SSL_ERROR_NO_CERTIFICATE_STR = "Unable to find the certificate or key necessary for authentication."; +const std::string SSL_ERROR_UNKNOWN = "Unknown NSS error code."; + +ErrorString::ErrorString() : code(PR_GetError()), buffer(new char[PR_GetErrorTextLength()]), used(PR_GetErrorText(buffer)) {} + +ErrorString::~ErrorString() +{ + delete[] buffer; +} + +std::string ErrorString::getString() const +{ + std::string msg = std::string(buffer, used); + if (!used) { + //seems most of the NSPR/NSS errors don't have text set for + //them, add a few specific ones in here. (TODO: more complete + //list?): + switch (code) { + case SSL_ERROR_BAD_CERT_DOMAIN: msg = SSL_ERROR_BAD_CERT_DOMAIN_STR; break; + case SSL_ERROR_BAD_CERT_ALERT: msg = SSL_ERROR_BAD_CERT_ALERT_STR; break; + case SEC_ERROR_BAD_DATABASE: msg = SEC_ERROR_BAD_DATABASE_STR; break; + case SSL_ERROR_NO_CERTIFICATE: msg = SSL_ERROR_NO_CERTIFICATE_STR; break; + default: msg = SSL_ERROR_UNKNOWN; break; + } + } + return str(format("%1% [%2%]") % msg % code); +} + +std::ostream& operator<<(std::ostream& out, const ErrorString& err) +{ + out << err.getString(); + return out; +} + + +}}} // namespace qpid::sys::ssl diff --git a/RC5/cpp/src/qpid/sys/ssl/check.h b/RC5/cpp/src/qpid/sys/ssl/check.h new file mode 100644 index 0000000000..6217a39429 --- /dev/null +++ b/RC5/cpp/src/qpid/sys/ssl/check.h @@ -0,0 +1,53 @@ +#ifndef QPID_SYS_SSL_CHECK_H +#define QPID_SYS_SSL_CHECK_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include <iostream> +#include <string> +#include <nspr4/nspr.h> +#include <nss3/nss.h> + +namespace qpid { +namespace sys { +namespace ssl { + +class ErrorString +{ + public: + ErrorString(); + ~ErrorString(); + std::string getString() const; + private: + const int code; + char* const buffer; + const size_t used; +}; + +std::ostream& operator<<(std::ostream& out, const ErrorString& err); + +}}} // namespace qpid::sys::ssl + + +#define NSS_CHECK(value) if (value != SECSuccess) { throw Exception(QPID_MSG("Failed: " << qpid::sys::ssl::ErrorString())); } +#define PR_CHECK(value) if (value != PR_SUCCESS) { throw Exception(QPID_MSG("Failed: " << qpid::sys::ssl::ErrorString())); } + +#endif /*!QPID_SYS_SSL_CHECK_H*/ diff --git a/RC5/cpp/src/qpid/sys/ssl/util.cpp b/RC5/cpp/src/qpid/sys/ssl/util.cpp new file mode 100644 index 0000000000..97b00f19de --- /dev/null +++ b/RC5/cpp/src/qpid/sys/ssl/util.cpp @@ -0,0 +1,118 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "util.h" +#include "check.h" +#include "qpid/Exception.h" +#include "qpid/sys/SystemInfo.h" + +#include <unistd.h> +#include <nspr4/nspr.h> +#include <nss3/nss.h> +#include <nss3/pk11pub.h> +#include <nss3/ssl.h> + +#include <iostream> +#include <fstream> +#include <boost/filesystem/operations.hpp> +#include <boost/filesystem/path.hpp> + +namespace qpid { +namespace sys { +namespace ssl { + +std::string defaultCertName() +{ + TcpAddress address; + if (SystemInfo::getLocalHostname(address)) { + return address.host; + } else { + return "localhost"; + } +} + +SslOptions::SslOptions() : qpid::Options("SSL Settings"), + certName(defaultCertName()), + exportPolicy(false) +{ + addOptions() + ("ssl-use-export-policy", optValue(exportPolicy), "Use NSS export policy") + ("ssl-cert-password-file", optValue(certPasswordFile, "PATH"), "File containing password to use for accessing certificate database") + ("ssl-cert-db", optValue(certDbPath, "PATH"), "Path to directory containing certificate database") + ("ssl-cert-name", optValue(certName, "NAME"), "Name of the certificate to use"); +} + +SslOptions& SslOptions::operator=(const SslOptions& o) +{ + certDbPath = o.certDbPath; + certName = o.certName; + certPasswordFile = o.certPasswordFile; + exportPolicy = o.exportPolicy; + return *this; +} + +char* promptForPassword(PK11SlotInfo*, PRBool retry, void*) +{ + if (retry) return 0; + //TODO: something else? + return PL_strdup(getpass("Please enter the password for accessing the certificate database:")); +} + +SslOptions SslOptions::global; + +char* readPasswordFromFile(PK11SlotInfo*, PRBool retry, void*) +{ + const std::string& passwordFile = SslOptions::global.certPasswordFile; + if (retry || passwordFile.empty() || !boost::filesystem::exists(passwordFile)) { + return 0; + } else { + std::ifstream file(passwordFile.c_str()); + std::string password; + file >> password; + return PL_strdup(password.c_str()); + } +} + +void initNSS(const SslOptions& options, bool server) +{ + SslOptions::global = options; + if (options.certPasswordFile.empty()) { + PK11_SetPasswordFunc(promptForPassword); + } else { + PK11_SetPasswordFunc(readPasswordFromFile); + } + NSS_CHECK(NSS_Init(options.certDbPath.c_str())); + if (options.exportPolicy) { + NSS_CHECK(NSS_SetExportPolicy()); + } else { + NSS_CHECK(NSS_SetDomesticPolicy()); + } + if (server) { + //use defaults for all args, TODO: may want to make this configurable + SSL_ConfigServerSessionIDCache(0, 0, 0, 0); + } +} + +void shutdownNSS() +{ + NSS_Shutdown(); +} + +}}} // namespace qpid::sys::ssl diff --git a/RC5/cpp/src/qpid/sys/ssl/util.h b/RC5/cpp/src/qpid/sys/ssl/util.h new file mode 100644 index 0000000000..f34adab7be --- /dev/null +++ b/RC5/cpp/src/qpid/sys/ssl/util.h @@ -0,0 +1,50 @@ +#ifndef QPID_SYS_SSL_UTIL_H +#define QPID_SYS_SSL_UTIL_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/Options.h" +#include <string> + +namespace qpid { +namespace sys { +namespace ssl { + +struct SslOptions : qpid::Options +{ + static SslOptions global; + + std::string certDbPath; + std::string certName; + std::string certPasswordFile; + bool exportPolicy; + + SslOptions(); + SslOptions& operator=(const SslOptions&); +}; + +void initNSS(const SslOptions& options, bool server = false); +void shutdownNSS(); + +}}} // namespace qpid::sys::ssl + +#endif /*!QPID_SYS_SSL_UTIL_H*/ diff --git a/RC5/cpp/src/qpid/sys/uuid.h b/RC5/cpp/src/qpid/sys/uuid.h new file mode 100644 index 0000000000..804ab34463 --- /dev/null +++ b/RC5/cpp/src/qpid/sys/uuid.h @@ -0,0 +1,28 @@ +#ifndef _sys_uuid_h +#define _sys_uuid_h + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifdef _WIN32 +# include "qpid/sys/windows/uuid.h" +#else +# include <uuid/uuid.h> +#endif /* _WIN32 */ + +#endif /* _sys_uuid_h */ diff --git a/RC5/cpp/src/qpid/sys/windows/AsynchIO.cpp b/RC5/cpp/src/qpid/sys/windows/AsynchIO.cpp new file mode 100644 index 0000000000..ca56efd8dd --- /dev/null +++ b/RC5/cpp/src/qpid/sys/windows/AsynchIO.cpp @@ -0,0 +1,741 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "AsynchIoResult.h" +#include "IoHandlePrivate.h" +#include "qpid/sys/AsynchIO.h" +#include "qpid/sys/Mutex.h" +#include "qpid/sys/Socket.h" +#include "qpid/sys/Thread.h" +#include "qpid/sys/Time.h" +#include "qpid/log/Statement.h" + +#include "check.h" + +#include <boost/thread/once.hpp> + +#include <queue> +#include <winsock2.h> +#include <mswsock.h> +#include <windows.h> + +#include <boost/bind.hpp> + +namespace { + + typedef qpid::sys::ScopedLock<qpid::sys::Mutex> QLock; + +/* + * We keep per thread state to avoid locking overhead. The assumption is that + * on average all the connections are serviced by all the threads so the state + * recorded in each thread is about the same. If this turns out not to be the + * case we could rebalance the info occasionally. + */ +QPID_TSS int threadReadTotal = 0; +QPID_TSS int threadMaxRead = 0; +QPID_TSS int threadReadCount = 0; +QPID_TSS int threadWriteTotal = 0; +QPID_TSS int threadWriteCount = 0; +QPID_TSS int64_t threadMaxReadTimeNs = 2 * 1000000; // start at 2ms + +/* + * The function pointers for AcceptEx and ConnectEx need to be looked up + * at run time. Make sure this is done only once. + */ +boost::once_flag lookUpAcceptExOnce = BOOST_ONCE_INIT; +LPFN_ACCEPTEX fnAcceptEx = 0; +typedef void (*lookUpFunc)(const qpid::sys::Socket &); + +void lookUpAcceptEx() { + SOCKET h = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + GUID guidAcceptEx = WSAID_ACCEPTEX; + DWORD dwBytes = 0; + WSAIoctl(h, + SIO_GET_EXTENSION_FUNCTION_POINTER, + &guidAcceptEx, + sizeof(guidAcceptEx), + &fnAcceptEx, + sizeof(fnAcceptEx), + &dwBytes, + NULL, + NULL); + closesocket(h); + if (fnAcceptEx == 0) + throw qpid::Exception(QPID_MSG("Failed to look up AcceptEx")); +} + +} + +namespace qpid { +namespace sys { + +/* + * Asynch Acceptor + * + * This implementation uses knowledge that the DispatchHandle handle member + * is derived from PollerHandle, which has a reference to the Socket. + * No dispatching features of DispatchHandle are used - we just use the + * conduit to the Socket. + * + * AsynchAcceptor uses an AsynchAcceptResult object to track completion + * and status of each accept operation outstanding. + */ + +class AsynchAcceptorPrivate { + + friend class AsynchAcceptResult; + +public: + AsynchAcceptorPrivate(const Socket& s, AsynchAcceptor::Callback callback); + ~AsynchAcceptorPrivate(); + void start(Poller::shared_ptr poller); + +private: + void restart(void); + + AsynchAcceptor::Callback acceptedCallback; + const Socket& socket; +}; + +AsynchAcceptor::AsynchAcceptor(const Socket& s, Callback callback) : + impl(new AsynchAcceptorPrivate(s, callback)) +{} + +AsynchAcceptor::~AsynchAcceptor() +{ delete impl; } + +void AsynchAcceptor::start(Poller::shared_ptr poller) { + impl->start(poller); +} + +AsynchAcceptorPrivate::AsynchAcceptorPrivate(const Socket& s, + AsynchAcceptor::Callback callback) + : acceptedCallback(callback), + socket(s) { + + s.setNonblocking(); +#if (BOOST_VERSION >= 103500) /* boost 1.35 or later reversed the args */ + boost::call_once(lookUpAcceptExOnce, lookUpAcceptEx); +#else + boost::call_once(lookUpAcceptEx, lookUpAcceptExOnce); +#endif +} + +AsynchAcceptorPrivate::~AsynchAcceptorPrivate(void) { + socket.close(); +} + +void AsynchAcceptorPrivate::start(Poller::shared_ptr poller) { + poller->addFd(PollerHandle(socket), Poller::INPUT); + restart (); +} + +void AsynchAcceptorPrivate::restart(void) { + DWORD bytesReceived = 0; // Not used, needed for AcceptEx API + AsynchAcceptResult *result = new AsynchAcceptResult(acceptedCallback, + this, + toFd(socket.impl)); + BOOL status; + status = ::fnAcceptEx(toFd(socket.impl), + toFd(result->newSocket->impl), + result->addressBuffer, + 0, + AsynchAcceptResult::SOCKADDRMAXLEN, + AsynchAcceptResult::SOCKADDRMAXLEN, + &bytesReceived, + result->overlapped()); + QPID_WINDOWS_CHECK_ASYNC_START(status); +} + + +AsynchAcceptResult::AsynchAcceptResult(AsynchAcceptor::Callback cb, + AsynchAcceptorPrivate *acceptor, + SOCKET listener) + : callback(cb), acceptor(acceptor), listener(listener) { + newSocket.reset (new Socket()); +} + +void AsynchAcceptResult::success(size_t /*bytesTransferred*/) { + ::setsockopt (toFd(newSocket->impl), + SOL_SOCKET, + SO_UPDATE_ACCEPT_CONTEXT, + (char*)&listener, + sizeof (listener)); + callback(*(newSocket.release())); + acceptor->restart (); + delete this; +} + +void AsynchAcceptResult::failure(int status) { + //if (status != WSA_OPERATION_ABORTED) + // Can there be anything else? ; + delete this; +} + +namespace windows { + +/* + * AsynchConnector does synchronous connects for now... to do asynch the + * IocpPoller will need some extension to register an event handle as a + * CONNECT-type "direction", the connect completion/result will need an + * event handle to associate with the connecting handle. But there's no + * time for that right now... + */ +class AsynchConnector : public qpid::sys::AsynchConnector { +private: + ConnectedCallback connCallback; + FailedCallback failCallback; + const Socket& socket; + +public: + AsynchConnector(const Socket& socket, + Poller::shared_ptr poller, + std::string hostname, + uint16_t port, + ConnectedCallback connCb, + FailedCallback failCb = 0); +}; + +AsynchConnector::AsynchConnector(const Socket& sock, + Poller::shared_ptr poller, + std::string hostname, + uint16_t port, + ConnectedCallback connCb, + FailedCallback failCb) + : connCallback(connCb), failCallback(failCb), socket(sock) { + socket.setNonblocking(); + try { + socket.connect(hostname, port); + connCallback(socket); + } catch(std::exception& e) { + if (failCallback) + failCallback(-1, std::string(e.what())); + socket.close(); + delete &socket; + } +} + +} // namespace windows + +AsynchConnector* qpid::sys::AsynchConnector::create(const Socket& s, + Poller::shared_ptr poller, + std::string hostname, + uint16_t port, + ConnectedCallback connCb, + FailedCallback failCb) +{ + return new qpid::sys::windows::AsynchConnector(s, + poller, + hostname, + port, + connCb, + failCb); +} + + +/* + * Asynch reader/writer + */ + +namespace windows { + +class AsynchIO : public qpid::sys::AsynchIO { +public: + AsynchIO(const Socket& s, + ReadCallback rCb, + EofCallback eofCb, + DisconnectCallback disCb, + ClosedCallback cCb = 0, + BuffersEmptyCallback eCb = 0, + IdleCallback iCb = 0); + ~AsynchIO(); + + // Methods inherited from qpid::sys::AsynchIO + + /** + * Notify the object is should delete itself as soon as possible. + */ + virtual void queueForDeletion(); + + /// Take any actions needed to prepare for working with the poller. + virtual void start(Poller::shared_ptr poller); + virtual void queueReadBuffer(BufferBase* buff); + virtual void unread(BufferBase* buff); + virtual void queueWrite(BufferBase* buff); + virtual void notifyPendingWrite(); + virtual void queueWriteClose(); + virtual bool writeQueueEmpty(); + virtual void startReading(); + + /** + * getQueuedBuffer returns a buffer from the buffer queue, if one is + * available. + * + * @retval Pointer to BufferBase buffer; 0 if none is available. + */ + virtual BufferBase* getQueuedBuffer(); + +private: + ReadCallback readCallback; + EofCallback eofCallback; + DisconnectCallback disCallback; + ClosedCallback closedCallback; + BuffersEmptyCallback emptyCallback; + IdleCallback idleCallback; + const Socket& socket; + Poller::shared_ptr poller; + + std::deque<BufferBase*> bufferQueue; + std::deque<BufferBase*> writeQueue; + /* The MSVC-supplied deque is not thread-safe; keep locks to serialize + * access to the buffer queue and write queue. + */ + Mutex bufferQueueLock; + + // Number of outstanding I/O operations. + volatile LONG opsInProgress; + // Is there a write in progress? + volatile bool writeInProgress; + // Deletion requested, but there are callbacks in progress. + volatile bool queuedDelete; + // Socket close requested, but there are operations in progress. + volatile bool queuedClose; + +private: + // Dispatch events that have completed. + void notifyEof(void); + void notifyDisconnect(void); + void notifyClosed(void); + void notifyBuffersEmpty(void); + void notifyIdle(void); + + /** + * Initiate a write of the specified buffer. There's no callback for + * write completion to the AsynchIO object. + */ + void startWrite(AsynchIO::BufferBase* buff); + + void close(void); + + /** + * readComplete is called when a read request is complete. + * + * @param result Results of the operation. + */ + void readComplete(AsynchReadResult *result); + + /** + * writeComplete is called when a write request is complete. + * + * @param result Results of the operation. + */ + void writeComplete(AsynchWriteResult *result); + + /** + * Queue of completions to run. This queue enforces the requirement + * from upper layers that only one thread at a time is allowed to act + * on any given connection. Once a thread is busy processing a completion + * on this object, other threads that dispatch completions queue the + * completions here for the in-progress thread to handle when done. + * Thus, any threads can dispatch a completion from the IocpPoller, but + * this class ensures that actual processing at the connection level is + * only on one thread at a time. + */ + std::queue<AsynchIoResult *> completionQueue; + volatile bool working; + Mutex completionLock; + + /** + * Called when there's a completion to process. + */ + void completion(AsynchIoResult *result); +}; + +AsynchIO::AsynchIO(const Socket& s, + ReadCallback rCb, + EofCallback eofCb, + DisconnectCallback disCb, + ClosedCallback cCb, + BuffersEmptyCallback eCb, + IdleCallback iCb) : + + readCallback(rCb), + eofCallback(eofCb), + disCallback(disCb), + closedCallback(cCb), + emptyCallback(eCb), + idleCallback(iCb), + socket(s), + opsInProgress(0), + writeInProgress(false), + queuedDelete(false), + queuedClose(false), + working(false) { +} + +struct deleter +{ + template <typename T> + void operator()(T *ptr){ delete ptr;} +}; + +AsynchIO::~AsynchIO() { + std::for_each( bufferQueue.begin(), bufferQueue.end(), deleter()); + std::for_each( writeQueue.begin(), writeQueue.end(), deleter()); +} + +void AsynchIO::queueForDeletion() { + queuedDelete = true; + if (opsInProgress > 0) { + QPID_LOG(info, "Delete AsynchIO queued; ops in progress"); + // AsynchIOHandler calls this then deletes itself; don't do any more + // callbacks. + readCallback = 0; + eofCallback = 0; + disCallback = 0; + closedCallback = 0; + emptyCallback = 0; + idleCallback = 0; + } + else { + delete this; + } +} + +void AsynchIO::start(Poller::shared_ptr poller0) { + poller = poller0; + poller->addFd(PollerHandle(socket), Poller::INPUT); + if (writeQueue.size() > 0) // Already have data queued for write + notifyPendingWrite(); + startReading(); +} + +void AsynchIO::queueReadBuffer(AsynchIO::BufferBase* buff) { + assert(buff); + buff->dataStart = 0; + buff->dataCount = 0; + QLock l(bufferQueueLock); + bufferQueue.push_back(buff); +} + +void AsynchIO::unread(AsynchIO::BufferBase* buff) { + assert(buff); + if (buff->dataStart != 0) { + memmove(buff->bytes, buff->bytes+buff->dataStart, buff->dataCount); + buff->dataStart = 0; + } + QLock l(bufferQueueLock); + bufferQueue.push_front(buff); +} + +void AsynchIO::queueWrite(AsynchIO::BufferBase* buff) { + assert(buff); + QLock l(bufferQueueLock); + writeQueue.push_back(buff); + if (!writeInProgress) + notifyPendingWrite(); +} + +void AsynchIO::notifyPendingWrite() { + // This method is generally called from a processing thread; transfer + // work on this to an I/O thread. Much of the upper layer code assumes + // that all I/O-related things happen in an I/O thread. + if (poller == 0) // Not really going yet... + return; + + InterlockedIncrement(&opsInProgress); + IOHandlePrivate *hp = + new IOHandlePrivate (INVALID_SOCKET, + boost::bind(&AsynchIO::completion, this, _1)); + IOHandle h(hp); + PollerHandle ph(h); + poller->addFd(ph, Poller::OUTPUT); +} + +void AsynchIO::queueWriteClose() { + queuedClose = true; + if (!writeInProgress) + notifyPendingWrite(); +} + +bool AsynchIO::writeQueueEmpty() { + QLock l(bufferQueueLock); + return writeQueue.size() == 0; +} + +/* + * Initiate a read operation. AsynchIO::readComplete() will be + * called when the read is complete and data is available. + */ +void AsynchIO::startReading() { + if (queuedDelete) + return; + + // (Try to) get a buffer; look on the front since there may be an + // "unread" one there with data remaining from last time. + AsynchIO::BufferBase *buff = 0; + { + QLock l(bufferQueueLock); + + if (!bufferQueue.empty()) { + buff = bufferQueue.front(); + assert(buff); + bufferQueue.pop_front(); + } + } + if (buff != 0) { + int readCount = buff->byteCount - buff->dataCount; + AsynchReadResult *result = + new AsynchReadResult(boost::bind(&AsynchIO::completion, this, _1), + buff, + readCount); + DWORD bytesReceived = 0, flags = 0; + InterlockedIncrement(&opsInProgress); + int status = WSARecv(toFd(socket.impl), + const_cast<LPWSABUF>(result->getWSABUF()), 1, + &bytesReceived, + &flags, + result->overlapped(), + 0); + if (status != 0) { + int error = WSAGetLastError(); + if (error != WSA_IO_PENDING) { + result->failure(error); + result = 0; // result is invalid here + return; + } + } + // On status 0 or WSA_IO_PENDING, completion will handle the rest. + } + else { + notifyBuffersEmpty(); + } + return; +} + +/** + * Return a queued buffer if there are enough to spare. + */ +AsynchIO::BufferBase* AsynchIO::getQueuedBuffer() { + QLock l(bufferQueueLock); + // Always keep at least one buffer (it might have data that was + // "unread" in it). + if (bufferQueue.size() <= 1) + return 0; + BufferBase* buff = bufferQueue.back(); + assert(buff); + bufferQueue.pop_back(); + return buff; +} + +void AsynchIO::notifyEof(void) { + if (eofCallback) + eofCallback(*this); +} + +void AsynchIO::notifyDisconnect(void) { + if (disCallback) + disCallback(*this); +} + +void AsynchIO::notifyClosed(void) { + if (closedCallback) + closedCallback(*this, socket); +} + +void AsynchIO::notifyBuffersEmpty(void) { + if (emptyCallback) + emptyCallback(*this); +} + +void AsynchIO::notifyIdle(void) { + if (idleCallback) + idleCallback(*this); +} + +/* + * Asynch reader/writer using overlapped I/O + */ + +void AsynchIO::startWrite(AsynchIO::BufferBase* buff) { + writeInProgress = true; + InterlockedIncrement(&opsInProgress); + int writeCount = buff->byteCount-buff->dataCount; + AsynchWriteResult *result = + new AsynchWriteResult(boost::bind(&AsynchIO::completion, this, _1), + buff, + buff->dataCount); + DWORD bytesSent = 0; + int status = WSASend(toFd(socket.impl), + const_cast<LPWSABUF>(result->getWSABUF()), 1, + &bytesSent, + 0, + result->overlapped(), + 0); + if (status != 0) { + int error = WSAGetLastError(); + if (error != WSA_IO_PENDING) { + result->failure(error); // Also decrements in-progress count + result = 0; // result is invalid here + return; + } + } + // On status 0 or WSA_IO_PENDING, completion will handle the rest. + return; +} + +/* + * Close the socket and callback to say we've done it + */ +void AsynchIO::close(void) { + socket.close(); + notifyClosed(); +} + +void AsynchIO::readComplete(AsynchReadResult *result) { + ++threadReadCount; + int status = result->getStatus(); + size_t bytes = result->getTransferred(); + if (status == 0 && bytes > 0) { + bool restartRead = true; // May not if receiver doesn't want more + threadReadTotal += bytes; + if (readCallback) + restartRead = readCallback(*this, result->getBuff()); + if (restartRead) + startReading(); + } + else { + // No data read, so put the buffer back. It may be partially filled, + // so "unread" it back to the front of the queue. + unread(result->getBuff()); + if (status == 0) + notifyEof(); + else + notifyDisconnect(); + } +} + +/* + * NOTE - this completion is called for completed writes and also when + * a write is desired. The difference is in the buff - if a write is desired + * the buff is 0. + */ +void AsynchIO::writeComplete(AsynchWriteResult *result) { + int status = result->getStatus(); + size_t bytes = result->getTransferred(); + AsynchIO::BufferBase *buff = result->getBuff(); + if (buff != 0) { + ++threadWriteCount; + writeInProgress = false; + if (status == 0 && bytes > 0) { + threadWriteTotal += bytes; + if (bytes < result->getRequested()) // Still more to go; resubmit + startWrite(buff); + else + queueReadBuffer(buff); // All done; back to the pool + } + else { + // An error... if it's a connection close, ignore it - it will be + // noticed and handled on a read completion any moment now. + // What to do with real error??? Save the Buffer? + } + } + + // If there are no writes outstanding, the priority is to write any + // remaining buffers first (either queued or via idle), then close the + // socket if that's queued. + // opsInProgress handled in completion() + if (!writeInProgress) { + bool writing = false; + { + QLock l(bufferQueueLock); + if (writeQueue.size() > 0) { + buff = writeQueue.front(); + assert(buff); + writeQueue.pop_front(); + startWrite(buff); + writing = true; + } + } + if (!writing) { + if (queuedClose) + close(); + else + notifyIdle(); + } + } + return; +} + +void AsynchIO::completion(AsynchIoResult *result) { + { + ScopedLock<Mutex> l(completionLock); + if (working) { + completionQueue.push(result); + return; + } + + // First thread in with something to do; note we're working then keep + // handling completions. + working = true; + while (result != 0) { + // New scope to unlock temporarily. + { + ScopedUnlock<Mutex> ul(completionLock); + AsynchReadResult *r = dynamic_cast<AsynchReadResult*>(result); + if (r != 0) + readComplete(r); + else { + AsynchWriteResult *w = + dynamic_cast<AsynchWriteResult*>(result); + writeComplete(w); + } + delete result; + result = 0; + InterlockedDecrement(&opsInProgress); + } + // Lock is held again. + if (completionQueue.empty()) + continue; + result = completionQueue.front(); + completionQueue.pop(); + } + working = false; + } + // Lock released; ok to delete if all is done. + if (opsInProgress == 0 && queuedDelete) + delete this; +} + +} // namespace windows + +AsynchIO* qpid::sys::AsynchIO::create(const Socket& s, + AsynchIO::ReadCallback rCb, + AsynchIO::EofCallback eofCb, + AsynchIO::DisconnectCallback disCb, + AsynchIO::ClosedCallback cCb, + AsynchIO::BuffersEmptyCallback eCb, + AsynchIO::IdleCallback iCb) +{ + return new qpid::sys::windows::AsynchIO(s, rCb, eofCb, disCb, cCb, eCb, iCb); +} + +}} // namespace qpid::sys diff --git a/RC5/cpp/src/qpid/sys/windows/AsynchIoResult.h b/RC5/cpp/src/qpid/sys/windows/AsynchIoResult.h new file mode 100755 index 0000000000..7db4e9c331 --- /dev/null +++ b/RC5/cpp/src/qpid/sys/windows/AsynchIoResult.h @@ -0,0 +1,185 @@ +#ifndef _windows_asynchIoResult_h +#define _windows_asynchIoResult_h + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/sys/AsynchIO.h" +#include "qpid/sys/Socket.h" +#include <memory.h> +#include <winsock2.h> +#include <ws2tcpip.h> + +namespace qpid { +namespace sys { + +/* + * AsynchIoResult defines the class that receives the result of an + * asynchronous I/O operation, either send/recv or accept/connect. + * + * Operation factories should set one of these up before beginning the + * operation. Poller knows how to dispatch completion to this class. + * This class must be subclassed for needed operations; this class provides + * an interface only and cannot be instantiated. + * + * This class is tied to Windows; it inherits from OVERLAPPED so that the + * IocpPoller can cast OVERLAPPED pointers back to AsynchIoResult and call + * the completion handler. + */ +class AsynchResult : private OVERLAPPED { +public: + LPOVERLAPPED overlapped(void) { return this; } + static AsynchResult* from_overlapped(LPOVERLAPPED ol) { + return static_cast<AsynchResult*>(ol); + } + virtual void success (size_t bytesTransferred) { + bytes = bytesTransferred; + status = 0; + complete(); + } + virtual void failure (int error) { + bytes = 0; + status = error; + complete(); + } + size_t getTransferred(void) const { return bytes; } + int getStatus(void) const { return status; } + +protected: + AsynchResult() : bytes(0), status(0) + { memset(overlapped(), 0, sizeof(OVERLAPPED)); } + ~AsynchResult() {} + virtual void complete(void) = 0; + + size_t bytes; + int status; +}; + +class AsynchAcceptorPrivate; +class AsynchAcceptResult : public AsynchResult { + + friend class AsynchAcceptorPrivate; + +public: + AsynchAcceptResult(AsynchAcceptor::Callback cb, + AsynchAcceptorPrivate *acceptor, + SOCKET listener); + virtual void success (size_t bytesTransferred); + virtual void failure (int error); + +private: + virtual void complete(void) {} // No-op for this class. + + std::auto_ptr<qpid::sys::Socket> newSocket; + AsynchAcceptor::Callback callback; + AsynchAcceptorPrivate *acceptor; + SOCKET listener; + + // AcceptEx needs a place to write the local and remote addresses + // when accepting the connection. Place those here; get enough for + // IPv6 addresses, even if the socket is IPv4. + enum { SOCKADDRMAXLEN = sizeof sockaddr_in6 + 16, + SOCKADDRBUFLEN = 2 * SOCKADDRMAXLEN }; + char addressBuffer[SOCKADDRBUFLEN]; +}; + +class AsynchIoResult : public AsynchResult { +public: + typedef boost::function1<void, AsynchIoResult *> Completer; + + virtual ~AsynchIoResult() {} + AsynchIO::BufferBase *getBuff(void) const { return iobuff; } + size_t getRequested(void) const { return requested; } + const WSABUF *getWSABUF(void) const { return &wsabuf; } + +protected: + void setBuff (AsynchIO::BufferBase *buffer) { iobuff = buffer; } + +protected: + AsynchIoResult(Completer cb, + AsynchIO::BufferBase *buff, size_t length) + : completionCallback(cb), iobuff(buff), requested(length) {} + + virtual void complete(void) = 0; + WSABUF wsabuf; + Completer completionCallback; + +private: + AsynchIO::BufferBase *iobuff; + size_t requested; // Number of bytes in original I/O request +}; + +class AsynchReadResult : public AsynchIoResult { + + // complete() updates buffer then does completion callback. + virtual void complete(void) { + getBuff()->dataCount += bytes; + completionCallback(this); + } + +public: + AsynchReadResult(AsynchIoResult::Completer cb, + AsynchIO::BufferBase *buff, + size_t length) + : AsynchIoResult(cb, buff, length) { + wsabuf.buf = buff->bytes + buff->dataCount; + wsabuf.len = length; + } +}; + +class AsynchWriteResult : public AsynchIoResult { + + // complete() updates buffer then does completion callback. + virtual void complete(void) { + AsynchIO::BufferBase *b = getBuff(); + b->dataStart += bytes; + b->dataCount -= bytes; + completionCallback(this); + } + +public: + AsynchWriteResult(AsynchIoResult::Completer cb, + AsynchIO::BufferBase *buff, + size_t length) + : AsynchIoResult(cb, buff, length) { + wsabuf.buf = buff ? buff->bytes : 0; + wsabuf.len = length; + } +}; + +class AsynchWriteWanted : public AsynchWriteResult { + + // complete() just does completion callback; no buffers used. + virtual void complete(void) { + completionCallback(this); + } + +public: + AsynchWriteWanted(AsynchIoResult::Completer cb) + : AsynchWriteResult(cb, 0, 0) { + wsabuf.buf = 0; + wsabuf.len = 0; + } +}; + +}} + +#endif /*!_windows_asynchIoResult_h*/ diff --git a/RC5/cpp/src/qpid/sys/windows/Condition.h b/RC5/cpp/src/qpid/sys/windows/Condition.h new file mode 100755 index 0000000000..979fae9b0a --- /dev/null +++ b/RC5/cpp/src/qpid/sys/windows/Condition.h @@ -0,0 +1,80 @@ +#ifndef _sys_windows_Condition_h +#define _sys_windows_Condition_h + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/sys/Mutex.h" +#include "qpid/sys/Time.h" + +#include <time.h> +#include <boost/noncopyable.hpp> +#include <boost/thread/condition.hpp> +#include <boost/thread/thread_time.hpp> +#include <windows.h> + +namespace qpid { +namespace sys { + +// Private Time related implementation details +void toPtime(boost::posix_time::ptime& pt, const AbsTime& t); + +/** + * A condition variable for thread synchronization. + */ +class Condition : private boost::noncopyable +{ + public: + inline Condition(); + inline ~Condition(); + inline void wait(Mutex&); + inline bool wait(Mutex&, const AbsTime& absoluteTime); + inline void notify(); + inline void notifyAll(); + + private: + boost::condition_variable_any condition; +}; + +Condition::Condition() { +} + +Condition::~Condition() { +} + +void Condition::wait(Mutex& mutex) { + condition.wait(mutex.mutex); +} + +bool Condition::wait(Mutex& mutex, const AbsTime& absoluteTime){ + return condition.timed_wait(mutex.mutex, absoluteTime.getPrivate()); +} + +void Condition::notify(){ + condition.notify_one(); +} + +void Condition::notifyAll(){ + condition.notify_all(); +} + +}} +#endif /*!_sys_windows_Condition_h*/ diff --git a/RC5/cpp/src/qpid/sys/windows/FileSysDir.cpp b/RC5/cpp/src/qpid/sys/windows/FileSysDir.cpp new file mode 100644 index 0000000000..88f1637d48 --- /dev/null +++ b/RC5/cpp/src/qpid/sys/windows/FileSysDir.cpp @@ -0,0 +1,53 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "qpid/sys/FileSysDir.h" +#include "qpid/sys/StrError.h" +#include "qpid/Exception.h" + +#include <sys/types.h> +#include <sys/stat.h> +#include <direct.h> +#include <errno.h> + +namespace qpid { +namespace sys { + +bool FileSysDir::exists (void) const +{ + const char *cpath = dirPath.c_str (); + struct _stat s; + if (::_stat(cpath, &s)) { + if (errno == ENOENT) { + return false; + } + throw qpid::Exception (strError(errno) + + ": Can't check directory: " + dirPath); + } + if (s.st_mode & _S_IFDIR) + return true; + throw qpid::Exception(dirPath + " is not a directory"); +} + +void FileSysDir::mkdir(void) +{ + if (::_mkdir(dirPath.c_str()) == -1) + throw Exception ("Can't create directory: " + dirPath); +} + +}} // namespace qpid::sys diff --git a/RC5/cpp/src/qpid/sys/windows/IOHandle.cpp b/RC5/cpp/src/qpid/sys/windows/IOHandle.cpp new file mode 100755 index 0000000000..ba544c8c90 --- /dev/null +++ b/RC5/cpp/src/qpid/sys/windows/IOHandle.cpp @@ -0,0 +1,42 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/sys/IOHandle.h" +#include "IoHandlePrivate.h" +#include <windows.h> + +namespace qpid { +namespace sys { + +SOCKET toFd(const IOHandlePrivate* h) +{ + return h->fd; +} + +IOHandle::IOHandle(IOHandlePrivate* h) : + impl(h) +{} + +IOHandle::~IOHandle() { + delete impl; +} + +}} // namespace qpid::sys diff --git a/RC5/cpp/src/qpid/sys/windows/IntegerTypes.h b/RC5/cpp/src/qpid/sys/windows/IntegerTypes.h new file mode 100755 index 0000000000..80168fab88 --- /dev/null +++ b/RC5/cpp/src/qpid/sys/windows/IntegerTypes.h @@ -0,0 +1,40 @@ +#ifndef QPID_SYS_WINDOWS_INTEGERTYPES_H +#define QPID_SYS_WINDOWS_INTEGERTYPES_H + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +typedef unsigned char uint8_t; +typedef char int8_t; +typedef unsigned short uint16_t; +typedef short int16_t; +typedef unsigned int uint32_t; +typedef int int32_t; +typedef unsigned __int64 uint64_t; +typedef __int64 int64_t; + +// Visual Studio doesn't define other common types, so set them up here too. +typedef int pid_t; +typedef int socklen_t; +typedef unsigned int size_t; +typedef int ssize_t; +typedef unsigned int uint; + +#endif /*!QPID_SYS_WINDOWS_INTEGERTYPES_H*/ diff --git a/RC5/cpp/src/qpid/sys/windows/IoHandlePrivate.h b/RC5/cpp/src/qpid/sys/windows/IoHandlePrivate.h new file mode 100755 index 0000000000..18e75047ed --- /dev/null +++ b/RC5/cpp/src/qpid/sys/windows/IoHandlePrivate.h @@ -0,0 +1,52 @@ +#ifndef _sys_windows_IoHandlePrivate_h +#define _sys_windows_IoHandlePrivate_h + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "AsynchIoResult.h" + +#include <winsock2.h> + +namespace qpid { +namespace sys { + +// Private fd related implementation details +// There should be either a valid socket handle or a completer callback. +// Handle is used to associate with poller's iocp; completer is used to +// inject a completion that will very quickly trigger a callback to the +// completer from an I/O thread. +class IOHandlePrivate { +public: + IOHandlePrivate(SOCKET f = INVALID_SOCKET, + AsynchIoResult::Completer cb = 0) : + fd(f), event(cb) + {} + + SOCKET fd; + AsynchIoResult::Completer event; +}; + +SOCKET toFd(const IOHandlePrivate* h); + +}} + +#endif /* _sys_windows_IoHandlePrivate_h */ diff --git a/RC5/cpp/src/qpid/sys/windows/IocpDispatcher.cpp b/RC5/cpp/src/qpid/sys/windows/IocpDispatcher.cpp new file mode 100755 index 0000000000..1a0f6ce927 --- /dev/null +++ b/RC5/cpp/src/qpid/sys/windows/IocpDispatcher.cpp @@ -0,0 +1,54 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/sys/Dispatcher.h" + +#include <assert.h> + +namespace qpid { +namespace sys { + +Dispatcher::Dispatcher(Poller::shared_ptr poller0) : + poller(poller0) { +} + +Dispatcher::~Dispatcher() { +} + +void Dispatcher::run() { + do { + Poller::Event event = poller->wait(); + + // Handle shutdown + switch (event.type) { + case Poller::SHUTDOWN: + return; + break; + case Poller::INVALID: // On any type of success or fail completion + break; + default: + // This should be impossible + assert(false); + } + } while (true); +} + +}} diff --git a/RC5/cpp/src/qpid/sys/windows/IocpPoller.cpp b/RC5/cpp/src/qpid/sys/windows/IocpPoller.cpp new file mode 100755 index 0000000000..44298ac8ea --- /dev/null +++ b/RC5/cpp/src/qpid/sys/windows/IocpPoller.cpp @@ -0,0 +1,176 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/sys/Poller.h" +#include "qpid/sys/Mutex.h" + +#include "AsynchIoResult.h" +#include "IoHandlePrivate.h" +#include "check.h" + +#include <winsock2.h> +#include <windows.h> + +#include <assert.h> +#include <vector> +#include <exception> + +namespace qpid { +namespace sys { + +class PollerHandlePrivate { + friend class Poller; + friend class PollerHandle; + + SOCKET fd; + AsynchIoResult::Completer cb; + + PollerHandlePrivate(SOCKET f, AsynchIoResult::Completer cb0 = 0) : + fd(f), cb(cb0) + { + } + +}; + +PollerHandle::PollerHandle(const IOHandle& h) : + impl(new PollerHandlePrivate(toFd(h.impl), h.impl->event)) +{} + +PollerHandle::~PollerHandle() { + delete impl; +} + +/** + * Concrete implementation of Poller to use the Windows I/O Completion + * port (IOCP) facility. + */ +class PollerPrivate { + friend class Poller; + + const HANDLE iocp; + + // The number of threads running the event loop. + volatile LONG threadsRunning; + + // Shutdown request is handled by setting isShutdown and injecting a + // well-formed completion event into the iocp. + bool isShutdown; + + PollerPrivate() : + iocp(::CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0)), + threadsRunning(0), + isShutdown(false) { + QPID_WINDOWS_CHECK_NULL(iocp); + } + + ~PollerPrivate() { + // It's probably okay to ignore any errors here as there can't be + // data loss + ::CloseHandle(iocp); + } +}; + +void Poller::addFd(PollerHandle& handle, Direction dir) { + HANDLE h = (HANDLE)(handle.impl->fd); + if (h != INVALID_HANDLE_VALUE) { + HANDLE iocpHandle = ::CreateIoCompletionPort (h, impl->iocp, 0, 0); + QPID_WINDOWS_CHECK_NULL(iocpHandle); + } + else { + AsynchWriteWanted *result = new AsynchWriteWanted(handle.impl->cb); + PostQueuedCompletionStatus(impl->iocp, 0, 0, result->overlapped()); + } +} + +void Poller::shutdown() { + // Allow sloppy code to shut us down more than once. + if (impl->isShutdown) + return; + ULONG_PTR key = 1; // Tell wait() it's a shutdown, not I/O + PostQueuedCompletionStatus(impl->iocp, 0, key, 0); +} + +// All no-ops... +void Poller::delFd(PollerHandle& handle) {} +void Poller::modFd(PollerHandle& handle, Direction dir) {} +void Poller::rearmFd(PollerHandle& handle) {} + +Poller::Event Poller::wait(Duration timeout) { + DWORD timeoutMs = 0; + DWORD numTransferred = 0; + ULONG_PTR completionKey = 0; + OVERLAPPED *overlapped = 0; + AsynchResult *result = 0; + + // Wait for either an I/O operation to finish (thus signaling the + // IOCP handle) or a shutdown request to be made (thus signaling the + // shutdown event). + if (timeout == TIME_INFINITE) + timeoutMs = INFINITE; + else + timeoutMs = static_cast<DWORD>(timeout / TIME_MSEC); + + InterlockedIncrement(&impl->threadsRunning); + bool goodOp = ::GetQueuedCompletionStatus (impl->iocp, + &numTransferred, + &completionKey, + &overlapped, + timeoutMs); + LONG remainingThreads = InterlockedDecrement(&impl->threadsRunning); + if (goodOp) { + // Dequeued a successful completion. If it's a posted packet from + // shutdown() the overlapped ptr is 0 and key is 1. Else downcast + // the OVERLAPPED pointer to an AsynchIoResult and call the + // completion handler. + if (overlapped == 0 && completionKey == 1) { + // If there are other threads still running this wait, re-post + // the completion. + if (remainingThreads > 0) + PostQueuedCompletionStatus(impl->iocp, 0, completionKey, 0); + return Event(0, SHUTDOWN); + } + + result = AsynchResult::from_overlapped(overlapped); + result->success (static_cast<size_t>(numTransferred)); + } + else { + if (overlapped != 0) { + // Dequeued a completion for a failed operation. Downcast back + // to the result object and inform it that the operation failed. + DWORD status = ::GetLastError(); + result = AsynchResult::from_overlapped(overlapped); + result->failure (static_cast<int>(status)); + } + } + return Event(0, INVALID); // TODO - this may need to be changed. + +} + +// Concrete constructors +Poller::Poller() : + impl(new PollerPrivate()) +{} + +Poller::~Poller() { + delete impl; +} + +}} diff --git a/RC5/cpp/src/qpid/sys/windows/LockFile.cpp b/RC5/cpp/src/qpid/sys/windows/LockFile.cpp new file mode 100755 index 0000000000..9804020167 --- /dev/null +++ b/RC5/cpp/src/qpid/sys/windows/LockFile.cpp @@ -0,0 +1,83 @@ +/* + * + * Copyright (c) 2008 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "qpid/sys/LockFile.h" +#include "check.h" + +#include <windows.h> + +namespace qpid { +namespace sys { + +class LockFilePrivate { + friend class LockFile; + + HANDLE fd; + +public: + LockFilePrivate(HANDLE f) : fd(f) {} +}; + +LockFile::LockFile(const std::string& path_, bool create) + : path(path_), created(create) { + + HANDLE h = CreateFile(path.c_str(), + GENERIC_READ|GENERIC_WRITE, + 0, /* Disable opens by any other attempter */ + 0, /* Default security */ + OPEN_ALWAYS, /* Create if needed */ + FILE_FLAG_DELETE_ON_CLOSE, /* Delete file when closed */ + NULL); + QPID_WINDOWS_CHECK_NOT(h, INVALID_HANDLE_VALUE); + impl.reset(new LockFilePrivate(h)); +} + +LockFile::~LockFile() { + if (impl) { + if (impl->fd != INVALID_HANDLE_VALUE) { + CloseHandle(impl->fd); + } + } +} + +pid_t LockFile::readPid(void) const { + if (!impl) + throw Exception("Lock file not open"); + + pid_t pid; + DWORD desired_read = sizeof(pid_t); + DWORD actual_read = 0; + if (!ReadFile(impl->fd, &pid, desired_read, &actual_read, 0)) { + throw Exception("Cannot read lock file " + path); + } + return pid; +} + +void LockFile::writePid(void) { + if (!impl) + throw Exception("Lock file not open"); + + pid_t pid = GetCurrentProcessId(); + DWORD desired_write = sizeof(pid_t); + DWORD written = 0; + if (!WriteFile(impl->fd, &pid, desired_write, &written, 0)) { + throw Exception("Cannot write lock file " + path); + } +} + +}} /* namespace qpid::sys */ diff --git a/RC5/cpp/src/qpid/sys/windows/Mutex.h b/RC5/cpp/src/qpid/sys/windows/Mutex.h new file mode 100755 index 0000000000..08de0712b9 --- /dev/null +++ b/RC5/cpp/src/qpid/sys/windows/Mutex.h @@ -0,0 +1,188 @@ +#ifndef _sys_windows_Mutex_h +#define _sys_windows_Mutex_h + +/* + * + * Copyright (c) 2008 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "check.h" + +#include <boost/version.hpp> +#if (BOOST_VERSION < 103500) +#error The Windows port requires Boost version 1.35.0 or later +#endif + +#include <boost/noncopyable.hpp> +#include <boost/thread/recursive_mutex.hpp> +#include <boost/thread/shared_mutex.hpp> +#include <boost/thread/thread_time.hpp> +#include <boost/thread/tss.hpp> + +namespace qpid { +namespace sys { + +class Condition; + +/** + * Mutex lock. + */ +class Mutex : private boost::noncopyable { + friend class Condition; + +public: + typedef ::qpid::sys::ScopedLock<Mutex> ScopedLock; + typedef ::qpid::sys::ScopedUnlock<Mutex> ScopedUnlock; + + inline Mutex(); + inline ~Mutex(); + inline void lock(); + inline void unlock(); + inline bool trylock(); + + +protected: + boost::recursive_mutex mutex; +}; + +/** + * RW lock. + */ +class RWlock : private boost::noncopyable { + friend class Condition; + +public: + typedef ::qpid::sys::ScopedRlock<RWlock> ScopedRlock; + typedef ::qpid::sys::ScopedWlock<RWlock> ScopedWlock; + + inline RWlock(); + inline ~RWlock(); + inline void wlock(); // will write-lock + inline void rlock(); // will read-lock + inline void unlock(); + inline void trywlock(); // will write-try + inline void tryrlock(); // will read-try + +protected: + boost::shared_mutex rwMutex; + boost::thread_specific_ptr<bool> haveWrite; + + inline bool &write (void); +}; + + +/** + * PODMutex is a POD, can be static-initialized with + * PODMutex m = QPID_PODMUTEX_INITIALIZER + */ +struct PODMutex +{ + typedef ::qpid::sys::ScopedLock<PODMutex> ScopedLock; + + inline void lock(); + inline void unlock(); + inline bool trylock(); + + // Must be public to be a POD: + boost::recursive_mutex mutex; +}; + +#define QPID_MUTEX_INITIALIZER 0 + +void PODMutex::lock() { + mutex.lock(); +} + +void PODMutex::unlock() { + mutex.unlock(); +} + +bool PODMutex::trylock() { + return mutex.try_lock(); +} + +Mutex::Mutex() { +} + +Mutex::~Mutex(){ +} + +void Mutex::lock() { + mutex.lock(); +} + +void Mutex::unlock() { + mutex.unlock(); +} + +bool Mutex::trylock() { + return mutex.try_lock(); +} + + +RWlock::RWlock() { +} + +RWlock::~RWlock(){ +} + +void RWlock::wlock() { + bool &writer = write(); + rwMutex.lock(); + writer = true; // Remember this thread has write lock held. +} + +void RWlock::rlock() { + bool &writer = write(); + rwMutex.lock_shared(); + writer = false; // Remember this thread has shared lock held. +} + +void RWlock::unlock() { + bool &writer = write(); + if (writer) + rwMutex.unlock(); + else + rwMutex.unlock_shared(); +} + +void RWlock::trywlock() { + bool &writer = write(); + // shared_mutex::try_lock() seems to not be available... emulate it with + // a timed lock(). + boost::system_time now = boost::get_system_time(); + if (rwMutex.timed_lock(now)) + writer = true; +} + +void RWlock::tryrlock() { + bool &writer = write(); + if (rwMutex.try_lock_shared()) + writer = false; +} + +bool & RWlock::write (void) { + // Accessing thread-specific and stack-local info, so no locks needed. + bool *writePtr = haveWrite.get(); + if (writePtr == 0) { + writePtr = new bool(false); + haveWrite.reset(writePtr); + } + return *writePtr; +} + +}} +#endif /*!_sys_windows_Mutex_h*/ diff --git a/RC5/cpp/src/qpid/sys/windows/Shlib.cpp b/RC5/cpp/src/qpid/sys/windows/Shlib.cpp new file mode 100644 index 0000000000..38027de93f --- /dev/null +++ b/RC5/cpp/src/qpid/sys/windows/Shlib.cpp @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/sys/Shlib.h" +#include "qpid/Exception.h" +#include "qpid/sys/windows/check.h" +#include <windows.h> + +namespace qpid { +namespace sys { + +void Shlib::load(const char* name) { + HMODULE h = LoadLibrary(name); + if (h == NULL) { + throw QPID_WINDOWS_ERROR(GetLastError()); + } + handle = static_cast<void*>(h); +} + +void Shlib::unload() { + if (handle) { + if (FreeLibrary(static_cast<HMODULE>(handle)) == 0) { + throw QPID_WINDOWS_ERROR(GetLastError()); + } + handle = 0; + } +} + +void* Shlib::getSymbol(const char* name) { + void* sym = GetProcAddress(static_cast<HMODULE>(handle), name); + if (sym == NULL) + throw QPID_WINDOWS_ERROR(GetLastError()); + return sym; +} + +}} // namespace qpid::sys diff --git a/RC5/cpp/src/qpid/sys/windows/Socket.cpp b/RC5/cpp/src/qpid/sys/windows/Socket.cpp new file mode 100755 index 0000000000..a9959bf43e --- /dev/null +++ b/RC5/cpp/src/qpid/sys/windows/Socket.cpp @@ -0,0 +1,329 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/sys/Socket.h" +#include "IoHandlePrivate.h" +#include "check.h" +#include "qpid/sys/Time.h" + +#include <cstdlib> +#include <string.h> +#include <iostream> +#include <memory.h> + +#include <winsock2.h> +#include <ws2tcpip.h> + +#include <boost/format.hpp> + +// Need to initialize WinSock. Ideally, this would be a singleton or embedded +// in some one-time initialization function. I tried boost singleton and could +// not get it to compile (and others located in google had the same problem). +// So, this simple static with an interlocked increment will do for known +// use cases at this time. Since this will only shut down winsock at process +// termination, there may be some problems with client programs that also +// expect to load and unload winsock, but we'll see... +// If someone does get an easy-to-use singleton sometime, converting to it +// may be preferable. + +namespace { + +static LONG volatile initialized = 0; + +class WinSockSetup { + // : public boost::details::pool::singleton_default<WinSockSetup> { + +public: + WinSockSetup() { + LONG timesEntered = InterlockedIncrement(&initialized); + if (timesEntered > 1) + return; + err = 0; + WORD wVersionRequested; + WSADATA wsaData; + + /* Request WinSock 2.2 */ + wVersionRequested = MAKEWORD(2, 2); + err = WSAStartup(wVersionRequested, &wsaData); + } + + ~WinSockSetup() { + WSACleanup(); + } + +public: + int error(void) const { return err; } + +protected: + DWORD err; +}; + +static WinSockSetup setup; + +} /* namespace */ + +namespace qpid { +namespace sys { + +namespace { + +std::string getName(SOCKET fd, bool local, bool includeService = false) +{ + sockaddr_in name; // big enough for any socket address + socklen_t namelen = sizeof(name); + if (local) { + QPID_WINSOCK_CHECK(::getsockname(fd, (sockaddr*)&name, &namelen)); + } else { + QPID_WINSOCK_CHECK(::getpeername(fd, (sockaddr*)&name, &namelen)); + } + + char servName[NI_MAXSERV]; + char dispName[NI_MAXHOST]; + if (includeService) { + if (int rc = ::getnameinfo((sockaddr*)&name, namelen, + dispName, sizeof(dispName), + servName, sizeof(servName), + NI_NUMERICHOST | NI_NUMERICSERV) != 0) + throw qpid::Exception(QPID_MSG(gai_strerror(rc))); + return std::string(dispName) + ":" + std::string(servName); + } else { + if (int rc = ::getnameinfo((sockaddr*)&name, namelen, + dispName, sizeof(dispName), + 0, 0, + NI_NUMERICHOST) != 0) + throw qpid::Exception(QPID_MSG(gai_strerror(rc))); + return dispName; + } +} + +std::string getService(SOCKET fd, bool local) +{ + sockaddr_in name; // big enough for any socket address + socklen_t namelen = sizeof(name); + + if (local) { + QPID_WINSOCK_CHECK(::getsockname(fd, (sockaddr*)&name, &namelen)); + } else { + QPID_WINSOCK_CHECK(::getpeername(fd, (sockaddr*)&name, &namelen)); + } + + char servName[NI_MAXSERV]; + if (int rc = ::getnameinfo((sockaddr*)&name, namelen, + 0, 0, + servName, sizeof(servName), + NI_NUMERICHOST | NI_NUMERICSERV) != 0) + throw qpid::Exception(QPID_MSG(gai_strerror(rc))); + return servName; +} +} // namespace + +Socket::Socket() : + IOHandle(new IOHandlePrivate) +{ + createTcp(); +} + +Socket::Socket(IOHandlePrivate* h) : + IOHandle(h) +{} + +void Socket::createTcp() const +{ + SOCKET& socket = impl->fd; + if (socket != INVALID_SOCKET) Socket::close(); + SOCKET s = ::socket (PF_INET, SOCK_STREAM, 0); + if (s == INVALID_SOCKET) throw QPID_WINDOWS_ERROR(WSAGetLastError()); + socket = s; +} + +void Socket::setTimeout(const Duration& interval) const +{ + const SOCKET& socket = impl->fd; + int64_t nanosecs = interval; + nanosecs /= (1000 * 1000); // nsecs -> usec -> msec + int msec = 0; + if (nanosecs > std::numeric_limits<int>::max()) + msec = std::numeric_limits<int>::max(); + else + msec = static_cast<int>(nanosecs); + setsockopt(socket, SOL_SOCKET, SO_SNDTIMEO, (char *)&msec, sizeof(msec)); + setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&msec, sizeof(msec)); +} + +void Socket::setNonblocking() const { + u_long nonblock = 1; + QPID_WINSOCK_CHECK(ioctlsocket(impl->fd, FIONBIO, &nonblock)); +} + +void Socket::connect(const std::string& host, uint16_t port) const +{ + std::stringstream portstream; + portstream << port << std::ends; + std::string portstr = portstream.str(); + std::stringstream namestream; + namestream << host << ":" << port; + connectname = namestream.str(); + + const SOCKET& socket = impl->fd; + // TODO: Be good to make this work for IPv6 as well as IPv4. Would require + // other changes, such as waiting to create the socket until after we + // have the address family. Maybe unbundle the translation of names here; + // use TcpAddress to resolve things and make this class take a TcpAddress + // and grab its address family to create the socket. + struct addrinfo hints; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; // We always creating AF_INET-only sockets. + hints.ai_socktype = SOCK_STREAM; // We always do TCP + addrinfo *addrs; + int status = getaddrinfo(host.c_str(), portstr.c_str(), &hints, &addrs); + if (status != 0) + throw Exception(QPID_MSG("Cannot resolve " << host << ": " << + gai_strerror(status))); + addrinfo *addr = addrs; + int error = 0; + WSASetLastError(0); + while (addr != 0) { + if ((::connect(socket, addr->ai_addr, addr->ai_addrlen) == 0) || + (WSAGetLastError() == WSAEWOULDBLOCK)) + break; + // Error... save this error code and see if there are other address + // to try before throwing the exception. + error = WSAGetLastError(); + addr = addr->ai_next; + } + freeaddrinfo(addrs); + if (error) + throw qpid::Exception(QPID_MSG(strError(error) << ": " << connectname)); +} + +void +Socket::close() const +{ + SOCKET& socket = impl->fd; + if (socket == INVALID_SOCKET) return; + QPID_WINSOCK_CHECK(closesocket(socket)); + socket = INVALID_SOCKET; +} + + +int Socket::write(const void *buf, size_t count) const +{ + const SOCKET& socket = impl->fd; + int sent = ::send(socket, (const char *)buf, count, 0); + if (sent == SOCKET_ERROR) + return -1; + return sent; +} + +int Socket::read(void *buf, size_t count) const +{ + const SOCKET& socket = impl->fd; + int received = ::recv(socket, (char *)buf, count, 0); + if (received == SOCKET_ERROR) + return -1; + return received; +} + +int Socket::listen(uint16_t port, int backlog) const +{ + const SOCKET& socket = impl->fd; + BOOL yes=1; + QPID_WINSOCK_CHECK(setsockopt(socket, SOL_SOCKET, SO_REUSEADDR, (char *)&yes, sizeof(yes))); + struct sockaddr_in name; + memset(&name, 0, sizeof(name)); + name.sin_family = AF_INET; + name.sin_port = htons(port); + name.sin_addr.s_addr = 0; + if (::bind(socket, (struct sockaddr*)&name, sizeof(name)) == SOCKET_ERROR) + throw Exception(QPID_MSG("Can't bind to port " << port << ": " << strError(WSAGetLastError()))); + if (::listen(socket, backlog) == SOCKET_ERROR) + throw Exception(QPID_MSG("Can't listen on port " << port << ": " << strError(WSAGetLastError()))); + + socklen_t namelen = sizeof(name); + QPID_WINSOCK_CHECK(::getsockname(socket, (struct sockaddr*)&name, &namelen)); + return ntohs(name.sin_port); +} + +Socket* Socket::accept(struct sockaddr *addr, socklen_t *addrlen) const +{ + SOCKET afd = ::accept(impl->fd, addr, addrlen); + if (afd != INVALID_SOCKET) + return new Socket(new IOHandlePrivate(afd)); + else if (WSAGetLastError() == EAGAIN) + return 0; + else throw QPID_WINDOWS_ERROR(WSAGetLastError()); +} + +std::string Socket::getSockname() const +{ + return getName(impl->fd, true); +} + +std::string Socket::getPeername() const +{ + return getName(impl->fd, false); +} + +std::string Socket::getPeerAddress() const +{ + if (!connectname.empty()) + return std::string (connectname); + return getName(impl->fd, false, true); +} + +std::string Socket::getLocalAddress() const +{ + return getName(impl->fd, true, true); +} + +uint16_t Socket::getLocalPort() const +{ + return atoi(getService(impl->fd, true).c_str()); +} + +uint16_t Socket::getRemotePort() const +{ + return atoi(getService(impl->fd, true).c_str()); +} + +int Socket::getError() const +{ + int result; + socklen_t rSize = sizeof (result); + + QPID_WINSOCK_CHECK(::getsockopt(impl->fd, SOL_SOCKET, SO_ERROR, (char *)&result, &rSize)); + return result; +} + +void Socket::setTcpNoDelay(bool nodelay) const +{ + if (nodelay) { + int flag = 1; + int result = setsockopt(impl->fd, + IPPROTO_TCP, + TCP_NODELAY, + (char *)&flag, + sizeof(flag)); + QPID_WINSOCK_CHECK(result); + } +} + +}} // namespace qpid::sys diff --git a/RC5/cpp/src/qpid/sys/windows/StrError.cpp b/RC5/cpp/src/qpid/sys/windows/StrError.cpp new file mode 100755 index 0000000000..9c1bfcd79c --- /dev/null +++ b/RC5/cpp/src/qpid/sys/windows/StrError.cpp @@ -0,0 +1,47 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/sys/StrError.h" +#include <string> +#include <string.h> +#include <windows.h> + +namespace qpid { +namespace sys { + +std::string strError(int err) { + const size_t bufsize = 512; + char buf[bufsize]; + if (0 == FormatMessage (FORMAT_MESSAGE_MAX_WIDTH_MASK + | FORMAT_MESSAGE_FROM_SYSTEM, + 0, + err, + 0, // Default language + buf, + bufsize, + 0)) + { + strerror_s (buf, bufsize, err); + } + return std::string(buf); +} + +}} diff --git a/RC5/cpp/src/qpid/sys/windows/SystemInfo.cpp b/RC5/cpp/src/qpid/sys/windows/SystemInfo.cpp new file mode 100755 index 0000000000..b887cac58b --- /dev/null +++ b/RC5/cpp/src/qpid/sys/windows/SystemInfo.cpp @@ -0,0 +1,160 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +/* GetNativeSystemInfo call requires _WIN32_WINNT 0x0501 or higher */ +#ifndef _WIN32_WINNT +# define _WIN32_WINNT 0x0501 +#endif + +#include "qpid/sys/IntegerTypes.h" +#include "qpid/sys/SystemInfo.h" + +#include <winsock2.h> +#include <ws2tcpip.h> +#include <windows.h> + +#ifndef HOST_NAME_MAX +# define HOST_NAME_MAX 256 +#endif + +namespace qpid { +namespace sys { + +long SystemInfo::concurrency() { + SYSTEM_INFO sys_info; + ::GetSystemInfo (&sys_info); + long activeProcessors = 0; + DWORD_PTR mask = sys_info.dwActiveProcessorMask; + while (mask != 0) { + if (mask & 1) + ++activeProcessors; + mask >>= 1; + } + return activeProcessors; +} + +bool SystemInfo::getLocalHostname (TcpAddress &address) { + char name[HOST_NAME_MAX]; + if (::gethostname(name, sizeof(name)) != 0) { + errno = WSAGetLastError(); + return false; + } + address.host = name; + return true; +} + +void SystemInfo::getLocalIpAddresses (uint16_t port, + std::vector<Address> &addrList) { + enum { MAX_URL_INTERFACES = 100 }; + static const std::string LOCALHOST("127.0.0.1"); + + SOCKET s = socket (PF_INET, SOCK_STREAM, 0); + if (s != INVALID_SOCKET) { + INTERFACE_INFO interfaces[MAX_URL_INTERFACES]; + DWORD filledBytes = 0; + WSAIoctl (s, + SIO_GET_INTERFACE_LIST, + 0, + 0, + interfaces, + sizeof (interfaces), + &filledBytes, + 0, + 0); + unsigned int interfaceCount = filledBytes / sizeof (INTERFACE_INFO); + for (unsigned int i = 0; i < interfaceCount; ++i) { + if (interfaces[i].iiFlags & IFF_UP) { + std::string addr(inet_ntoa(interfaces[i].iiAddress.AddressIn.sin_addr)); + if (addr != LOCALHOST) + addrList.push_back(TcpAddress(addr, port)); + } + } + closesocket (s); + } +} + +void SystemInfo::getSystemId (std::string &osName, + std::string &nodeName, + std::string &release, + std::string &version, + std::string &machine) +{ + osName = "Microsoft Windows"; + + char node[MAX_COMPUTERNAME_LENGTH + 1]; + DWORD nodelen = MAX_COMPUTERNAME_LENGTH + 1; + GetComputerName (node, &nodelen); + nodeName = node; + + OSVERSIONINFOEX vinfo; + vinfo.dwOSVersionInfoSize = sizeof(vinfo); + GetVersionEx ((OSVERSIONINFO *)&vinfo); + + SYSTEM_INFO sinfo; + GetNativeSystemInfo(&sinfo); + + switch(vinfo.dwMajorVersion) { + case 5: + switch(vinfo.dwMinorVersion) { + case 0: + release ="2000"; + break; + case 1: + release = "XP"; + break; + case 2: + if (sinfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64 || + sinfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_IA64) + release = "XP-64"; + else + release = "Server 2003"; + break; + default: + release = "Windows"; + } + break; + case 6: + if (vinfo.wProductType == VER_NT_SERVER) + release = "Server 2008"; + else + release = "Vista"; + break; + default: + release = "Microsoft Windows"; + } + version = vinfo.szCSDVersion; + + switch(sinfo.wProcessorArchitecture) { + case PROCESSOR_ARCHITECTURE_AMD64: + machine = "x86-64"; + break; + case PROCESSOR_ARCHITECTURE_IA64: + machine = "IA64"; + break; + case PROCESSOR_ARCHITECTURE_INTEL: + machine = "x86"; + break; + default: + machine = "unknown"; + break; + } +} + +}} // namespace qpid::sys diff --git a/RC5/cpp/src/qpid/sys/windows/Thread.cpp b/RC5/cpp/src/qpid/sys/windows/Thread.cpp new file mode 100755 index 0000000000..6d5d78393e --- /dev/null +++ b/RC5/cpp/src/qpid/sys/windows/Thread.cpp @@ -0,0 +1,88 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/sys/Thread.h" +#include "qpid/sys/Runnable.h" +#include "check.h" + +#include <process.h> +#include <windows.h> + +namespace { +unsigned __stdcall runRunnable(void* p) +{ + static_cast<qpid::sys::Runnable*>(p)->run(); + _endthreadex(0); + return 0; +} +} + +namespace qpid { +namespace sys { + +class ThreadPrivate { + friend class Thread; + + HANDLE threadHandle; + unsigned threadId; + + ThreadPrivate(Runnable* runnable) { + uintptr_t h = _beginthreadex(0, + 0, + runRunnable, + runnable, + 0, + &threadId); + QPID_WINDOWS_CHECK_CRT_NZ(h); + threadHandle = reinterpret_cast<HANDLE>(h); + } + + ThreadPrivate() + : threadHandle(GetCurrentThread()), threadId(GetCurrentThreadId()) {} +}; + +Thread::Thread() {} + +Thread::Thread(Runnable* runnable) : impl(new ThreadPrivate(runnable)) {} + +Thread::Thread(Runnable& runnable) : impl(new ThreadPrivate(&runnable)) {} + +void Thread::join() { + if (impl) { + DWORD status = WaitForSingleObject (impl->threadHandle, INFINITE); + QPID_WINDOWS_CHECK_NOT(status, WAIT_FAILED); + CloseHandle (impl->threadHandle); + impl->threadHandle = 0; + } +} + +unsigned long Thread::id() { + return impl ? impl->threadId : 0; +} + +/* static */ +Thread Thread::current() { + Thread t; + t.impl.reset(new ThreadPrivate()); + return t; +} + +}} /* qpid::sys */ diff --git a/RC5/cpp/src/qpid/sys/windows/Time.cpp b/RC5/cpp/src/qpid/sys/windows/Time.cpp new file mode 100644 index 0000000000..2390826831 --- /dev/null +++ b/RC5/cpp/src/qpid/sys/windows/Time.cpp @@ -0,0 +1,90 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/sys/Time.h" +#include <ostream> +#include <boost/date_time/posix_time/posix_time.hpp> +#include <boost/thread/thread_time.hpp> +#include <windows.h> + +using namespace boost::posix_time; + +namespace qpid { +namespace sys { + +AbsTime::AbsTime(const AbsTime& t, const Duration& d) { + if (d == Duration::max()) { + timepoint = ptime(max_date_time); + } + else { + time_duration td = microseconds(d.nanosecs / 1000); + timepoint = t.timepoint + td; + } +} + +AbsTime AbsTime::FarFuture() { + AbsTime ff; + ptime maxd(max_date_time); + ff.timepoint = maxd; + return ff; +} + +AbsTime AbsTime::now() { + AbsTime time_now; + time_now.timepoint = boost::get_system_time(); + return time_now; +} + +Duration::Duration(const AbsTime& time0) : nanosecs(0) { + time_period p(ptime(min_date_time), time0.timepoint); + nanosecs = p.length().total_nanoseconds(); +} + +Duration::Duration(const AbsTime& start, const AbsTime& finish) { + time_duration d = finish.timepoint - start.timepoint; + nanosecs = d.total_nanoseconds(); +} + +std::ostream& operator<<(std::ostream& o, const Duration& d) { + return o << int64_t(d) << "ns"; +} + +std::ostream& operator<<(std::ostream& o, const AbsTime& t) { + std::string time_string = to_simple_string(t.timepoint); + return o << time_string; +} + +void toPtime(ptime& pt, const AbsTime& t) { + pt = t.getPrivate(); +} + +void sleep(int secs) { + ::Sleep(secs * 1000); +} + +void usleep(uint64_t usecs) { + DWORD msecs = usecs / 1000; + if (msecs == 0) + msecs = 1; + ::Sleep(msecs); +} + +}} diff --git a/RC5/cpp/src/qpid/sys/windows/Time.h b/RC5/cpp/src/qpid/sys/windows/Time.h new file mode 100644 index 0000000000..49b3c4bab3 --- /dev/null +++ b/RC5/cpp/src/qpid/sys/windows/Time.h @@ -0,0 +1,36 @@ +#ifndef QPID_SYS_WINDOWS_TIME_H +#define QPID_SYS_WINDOWS_TIME_H + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <boost/date_time/posix_time/posix_time.hpp> + +namespace qpid { +namespace sys { + +/** + * Class to represent an instant in time. Boost has this stuff already done + * so just reuse it. We can also grab this for quick use with the Condition + * wait operations. + */ +typedef boost::posix_time::ptime TimePrivate; + +}} // namespace qpid::sys + +#endif /*!QPID_SYS_WINDOWS_TIME_H*/ diff --git a/RC5/cpp/src/qpid/sys/windows/check.h b/RC5/cpp/src/qpid/sys/windows/check.h new file mode 100755 index 0000000000..aba38814b2 --- /dev/null +++ b/RC5/cpp/src/qpid/sys/windows/check.h @@ -0,0 +1,48 @@ +#ifndef _windows_check_h +#define _windows_check_h + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/Exception.h" +#include "qpid/sys/StrError.h" + +#define QPID_WINDOWS_ERROR(ERRVAL) qpid::Exception(QPID_MSG(qpid::sys::strError(ERRVAL))) +#define QPID_WINDOWS_CRT_ERROR(ERRNO) qpid::Exception(QPID_MSG(qpid::sys::strError(ERRNO))) + +/** THROW QPID_WINDOWS_ERROR(::GetLastError()) if RESULT is NULL */ +#define QPID_WINDOWS_CHECK_NULL(RESULT) \ + if ((RESULT) == NULL) throw QPID_WINDOWS_ERROR((::GetLastError())) + +#define QPID_WINDOWS_CHECK_NOT(RESULT,VAL) \ + if ((RESULT) == (VAL)) throw QPID_WINDOWS_ERROR((::GetLastError())) + +#define QPID_WINDOWS_CHECK_ASYNC_START(STATUS) \ + if (!(STATUS) && ::WSAGetLastError() != ERROR_IO_PENDING) \ + throw QPID_WINDOWS_ERROR((::WSAGetLastError())) + +#define QPID_WINDOWS_CHECK_CRT_NZ(VAL) \ + if ((VAL) == 0) throw QPID_WINDOWS_CRT_ERROR(errno) + +#define QPID_WINSOCK_CHECK(OP) \ + if ((OP) == SOCKET_ERROR) throw QPID_WINDOWS_ERROR((::WSAGetLastError())) + +#endif /*!_windows_check_h*/ diff --git a/RC5/cpp/src/qpid/sys/windows/uuid.cpp b/RC5/cpp/src/qpid/sys/windows/uuid.cpp new file mode 100644 index 0000000000..92f60e04b1 --- /dev/null +++ b/RC5/cpp/src/qpid/sys/windows/uuid.cpp @@ -0,0 +1,54 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "uuid.h" + +#include <string.h> + +void uuid_clear (uuid_t uu) { + UuidCreateNil (reinterpret_cast<UUID*>(uu)); +} + +void uuid_copy (uuid_t dst, const uuid_t src) { + memcpy (dst, src, qpid::sys::UuidSize); +} + +void uuid_generate (uuid_t out) { + UuidCreate (reinterpret_cast<UUID*>(out)); +} + +int uuid_is_null (const uuid_t uu) { + RPC_STATUS unused; + return UuidIsNil ((UUID*)uu, &unused); +} + +int uuid_parse (const char *in, uuid_t uu) { + return UuidFromString ((unsigned char*)in, (UUID*)uu) == RPC_S_OK ? 0 : -1; +} + +void uuid_unparse (const uuid_t uu, char *out) { + unsigned char *formatted; + if (UuidToString((UUID*)uu, &formatted) == RPC_S_OK) { + strncpy_s (out, 36+1, (char*)formatted, _TRUNCATE); + RpcStringFree(&formatted); + } +} + diff --git a/RC5/cpp/src/qpid/sys/windows/uuid.h b/RC5/cpp/src/qpid/sys/windows/uuid.h new file mode 100644 index 0000000000..a44ef2e9a3 --- /dev/null +++ b/RC5/cpp/src/qpid/sys/windows/uuid.h @@ -0,0 +1,41 @@ +#ifndef _sys_windows_uuid_h +#define _sys_windows_uuid_h + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include <Rpc.h> + +#ifdef uuid_t /* Done in rpcdce.h */ +# undef uuid_t +#endif +#include <qpid/sys/IntegerTypes.h> +namespace qpid { namespace sys { const size_t UuidSize = 16; }} +typedef uint8_t uuid_t[qpid::sys::UuidSize]; + +void uuid_clear (uuid_t uu); +void uuid_copy (uuid_t dst, const uuid_t src); +void uuid_generate (uuid_t out); +int uuid_is_null (const uuid_t uu); // Returns 1 if null, else 0 +int uuid_parse (const char *in, uuid_t uu); // Returns 0 on success, else -1 +void uuid_unparse (const uuid_t uu, char *out); + +#endif /*!_sys_windows_uuid_h*/ |
