diff --git a/indra/llcommon/aisyncclient.cpp b/indra/llcommon/aisyncclient.cpp deleted file mode 100644 index 18ca33edc..000000000 --- a/indra/llcommon/aisyncclient.cpp +++ /dev/null @@ -1,538 +0,0 @@ -/** - * @file aisyncclient.cpp - * - * Copyright (c) 2013, Aleric Inglewood. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution. - * - * CHANGELOG - * and additional copyright holders. - * - * 05/12/2013 - * - Initial version, written by Aleric Inglewood @ SL - */ - -#include "sys.h" -#include "aisyncclient.h" -#include -#include "debug.h" - -typedef std::deque > servers_type; -static servers_type servers[syncgroup_size]; - -//static -template -void AISyncServer::register_client(AISyncClient_ServerPtr* client) -{ -#ifdef DEBUG_SYNCOUTPUT - DoutEntering(dc::notice, "AISyncServer::register_client(" << client << ")"); -#endif - - // Determine which server to use. - boost::intrusive_ptr server; - int expired = 0; - AISyncKey const sync_key(client->sync_key_hash()); - for (servers_type::iterator server_iter = servers[syncgroup].begin(); server_iter != servers[syncgroup].end(); ++server_iter) - { - boost::intrusive_ptr& server_ptr = *server_iter; - if (server_ptr->mSyncKey.expired()) - { - ++expired; - // If the server only contains a single client, then unregister it and put the server (back) in the server cache. - if (server_ptr->get_refcount() == 2) - { - server_ptr->unregister_last_client(); - AISyncServer::dispose_server(server_ptr); - } - continue; - } - if (server_ptr->mSyncKey.matches(sync_key)) - { - server = server_ptr; - break; - } - } - // Remove servers with expired keys. - if (expired) - { - if (expired == servers[syncgroup].size()) - { - servers[syncgroup].clear(); - } - else - { - servers[syncgroup].erase(servers[syncgroup].begin(), servers[syncgroup].begin() + expired); - } - } - if (!server) - { - AISyncServer::create_server(server, sync_key); - servers[syncgroup].push_back(server); - } - - // Sanity check: the client should never already be registered. - llassert(!client->mSyncServer); - // Recover from assertion failure. - if (client->mSyncServer) - { - if (client->mSyncServer == server) - { - return; - } - client->mSyncServer->unregister_client(client, syncgroup); - register_client(client); - return; - } - - // Obviously... - llassert(!client->mReadyEvents); - - // Check if the current clients are all ready: adding a new one will cause the group to become not-ready. - bool all_old_clients_are_ready = server->mNrReady && !((server->mNrClients - server->mNrReady) & synccountmask1); - // Add new client to the group. - client->mSyncServer = server; - server->mNrClients += syncevents; - llassert((server->mNrClients & syncoverflowbits) == 0); - if (all_old_clients_are_ready) - { - server->trigger_not_ready(); // Tell all old clients that the group is not ready. - } - server->mClients.push_back(client); // Actually add the new client to the list. -} - -void AISyncServer::unregister_client(AISyncClient_ServerPtr* client, syncgroups syncgroup) -{ -#ifdef DEBUG_SYNCOUTPUT - DoutEntering(dc::notice, "unregister_client(" << client << ", " << syncgroup << "), with this = " << this); -#endif - - // The client must be registered with this server. - llassert(client->mSyncServer == this); - // A client may only be unregistered after it was marked not-ready for all events. - llassert(!client->mReadyEvents); - // Run over all registered clients. - for (client_list_type::iterator client_iter = mClients.begin(); client_iter != mClients.end(); ++client_iter) - { - // Found it? - if (*client_iter == client) - { - mClients.erase(client_iter); - // Are the remaining clients ready? - if (mNrReady && !((mNrClients - syncevents - mNrReady) & synccountmask1)) - { - trigger_ready(); - } - mNrClients -= syncevents; - llassert((mNrClients & syncoverflowbits) == 0); - client->mSyncServer.reset(); // This might delete the current object. - break; - } - } - // The client must have been found. - llassert(!client->mSyncServer); -} - -void AISyncServer::unregister_last_client(void) -{ -#ifdef DEBUG_SYNCOUTPUT - DoutEntering(dc::notice, "unregister_last_client(), with this = " << this); -#endif - - // This function may only be called for servers with exactly one client. - llassert(mClients.size() == 1); - AISyncClient_ServerPtr* client = *mClients.begin(); -#ifdef DEBUG_SYNCOUTPUT - Dout(dc::notice, "unregistering client " << client); -#endif - mClients.clear(); - mNrClients -= syncevents; - mNrReady = 0; - llassert((mNrClients & syncoverflowbits) == 0); - client->mSyncServer.reset(); - client->deregistered(); -} - -synceventset AISyncServer::events_with_all_clients_ready(void) const -{ - synccount nrNotReady = mNrClients - mNrReady; - synceventset result1 = !(nrNotReady & synccountmask1) ? syncevent1 : 0; - synceventset result2 = !(nrNotReady & synccountmask2) ? syncevent2 : 0; - synceventset result3 = !(nrNotReady & synccountmask3) ? syncevent3 : 0; - synceventset result4 = !(nrNotReady & synccountmask4) ? syncevent4 : 0; - result1 |= result2; - result3 |= result4; - return result1 | result3; -} - -synceventset AISyncServer::events_with_at_least_one_client_ready(void) const -{ - synceventset result1 = (mNrReady & synccountmask1) ? syncevent1 : 0; - synceventset result2 = (mNrReady & synccountmask2) ? syncevent2 : 0; - synceventset result3 = (mNrReady & synccountmask3) ? syncevent3 : 0; - synceventset result4 = (mNrReady & synccountmask4) ? syncevent4 : 0; - result1 |= result2; - result3 |= result4; - return result1 | result3; -} - -#ifdef CWDEBUG -struct SyncEventSet { - synceventset mBits; - SyncEventSet(synceventset bits) : mBits(bits) { } -}; - -std::ostream& operator<<(std::ostream& os, SyncEventSet const& ses) -{ - os << ((ses.mBits & syncevent4) ? '1' : '0'); - os << ((ses.mBits & syncevent3) ? '1' : '0'); - os << ((ses.mBits & syncevent2) ? '1' : '0'); - os << ((ses.mBits & syncevent1) ? '1' : '0'); - return os; -} -#endif - -bool AISyncServer::ready(synceventset events, synceventset yesno/*,*/ ASSERT_ONLY_COMMA(AISyncClient_ServerPtr* client)) -{ -#ifdef DEBUG_SYNCOUTPUT - DoutEntering(dc::notice, "AISyncServer::ready(" << SyncEventSet(events) << ", " << SyncEventSet(yesno) << ", " << client << ")"); -#endif - - synceventset added_events = events & yesno; - synceventset removed_events = events & ~yesno; - // May not add events that are already ready. - llassert(!(client->mReadyEvents & added_events)); - // Cannot remove events that weren't ready. - llassert((client->mReadyEvents & removed_events) == removed_events); - // Were all clients ready for event 1? - bool ready_before = !((mNrClients - mNrReady) & synccountmask1); - // Update mNrReady counters. - mNrReady += added_events; - mNrReady -= removed_events; - // Test for under and overflow, this limits the maximum number of clients to 127 instead of 255, but well :p. - llassert((mNrReady & syncoverflowbits) == 0); - // Are all clients ready for event 1? - bool ready_after = !((mNrClients - mNrReady) & synccountmask1); - if (ready_before && !ready_after) - { - trigger_not_ready(); - } -#ifdef SHOW_ASSERT - // Update debug administration. - client->mReadyEvents ^= events; -#ifdef DEBUG_SYNCOUTPUT - Dout(dc::notice, "Client " << client << " now has ready: " << SyncEventSet(client->mReadyEvents)); -#endif -#endif - if (!ready_before && ready_after) - { - trigger_ready(); - } -} - -void AISyncServer::trigger_ready(void) -{ - for (client_list_type::iterator client_iter = mClients.begin(); client_iter != mClients.end(); ++client_iter) - { - llassert(((*client_iter)->mReadyEvents & syncevent1)); - (*client_iter)->event1_ready(); - } -} - -void AISyncServer::trigger_not_ready(void) -{ - for (client_list_type::iterator client_iter = mClients.begin(); client_iter != mClients.end(); ++client_iter) - { - llassert(((*client_iter)->mReadyEvents & syncevent1)); - (*client_iter)->event1_not_ready(); - } -} - -void intrusive_ptr_add_ref(AISyncServer* server) -{ - server->mNrClients += syncrefcountunit; - llassert((server->mNrClients & syncoverflowbits) == 0); -} - -void intrusive_ptr_release(AISyncServer* server) -{ - llassert(server->mNrClients >= syncrefcountunit); - server->mNrClients -= syncrefcountunit; - // If there are no more pointers pointing to this server, then obviously it can't have any registered clients. - llassert(server->mNrClients >= syncrefcountunit || server->mNrClients == 0); - if (server->mNrClients == 0) - { - delete server; - } -} - -//static -sync_key_hash_type const AISyncKey::sNoRequirementHash = 0x91b42f98a9fef15cULL; - - -//============================================================================= -// SYNC_TESTSUITE -//============================================================================= - -#ifdef SYNC_TESTSUITE -#include -#include -#include -#include - -//static -U32 LLFrameTimer::sFrameCount; - -double innerloop_count = 0; - -double LLFrameTimer::getCurrentTime() -{ - return innerloop_count * 0.001; -} - -class TestsuiteServerBase : public AISyncServer -{ - public: - TestsuiteServerBase(AISyncKey const& sync_key) : AISyncServer(sync_key) { } - client_list_type& clients(void) { return mClients; } -}; - -class TestsuiteServer1 : public TestsuiteServerBase -{ - public: - TestsuiteServer1(AISyncKey const& sync_key) : TestsuiteServerBase(sync_key) { } -}; - -// Specialitations that link TestsuiteServer1 to syncgroup_test1. -// -//static -template<> -void AISyncServer::create_server(boost::intrusive_ptr& server, AISyncKey const& sync_key) -{ - AISyncServerCache::create_server(server, sync_key); -} -//static -template<> -void AISyncServer::dispose_server(boost::intrusive_ptr& server) -{ - AISyncServerCache::dispose_server(server); -} - -class TestsuiteServer2 : public TestsuiteServerBase -{ - public: - TestsuiteServer2(AISyncKey const& sync_key) : TestsuiteServerBase(sync_key) { } -}; - -// Specialitations that link TestsuiteServer2 to syncgroup_test2. -// -//static -template<> -void AISyncServer::create_server(boost::intrusive_ptr& server, AISyncKey const& sync_key) -{ - AISyncServerCache::create_server(server, sync_key); -} -//static -template<> -void AISyncServer::dispose_server(boost::intrusive_ptr& server) -{ - AISyncServerCache::dispose_server(server); -} - -template -class TestsuiteClient : public AISyncClient -{ - private: - int mIndex; - bool mRequestedRegistered; - synceventset mRequestedReady; - bool mActualReady1; - - public: - TestsuiteClient() : mIndex(-1), mRequestedRegistered(false), mRequestedReady(0), mActualReady1(false) { } - ~TestsuiteClient() { this->ready(mRequestedReady, (synceventset)0); } - - void setIndex(int index) { mIndex = index; } - - protected: - /*virtual*/ void event1_ready(void) - { - llassert(!mActualReady1); - mActualReady1 = true; - } - - /*virtual*/ void event1_not_ready(void) - { - llassert(mActualReady1); - mActualReady1 = false; - } - - /*virtual*/ sync_key_hash_type sync_key_hash(void) const - { - // Sync odd clients with eachother, and even clients with eachother. - return 0xb3c919ff + (mIndex & 1); - } - - // This is called when the server expired and we're the only client on it. - /*virtual*/ void deregistered(void) - { -#ifdef DEBUG_SYNCOUTPUT - DoutEntering(dc::notice, "TestsuiteClient<" << syncgroup << ">::deregistered(), with this = " << this); -#endif - mRequestedRegistered = false; - mRequestedReady = 0; - mActualReady1 = false; - this->mReadyEvents = 0; - } - - private: - bool is_registered(void) const { return !!this->mSyncServer; } - - public: - void change_state(unsigned long r); - bool getRequestedRegistered(void) const { return mRequestedRegistered; } - synceventset getRequestedReady(void) const { return mRequestedReady; } -}; - -TestsuiteClient* client1p; -TestsuiteClient* client2p; - -int const number_of_clients_per_syncgroup = 8; - -template -void TestsuiteClient::change_state(unsigned long r) -{ - bool change_registered = r & 1; - r >>= 1; - synceventset toggle_events = ((r & 1) ? syncevent1 : 0) | ((r & 2) ? syncevent2 : 0) | ((r & 4) ? syncevent3 : 0) | ((r & 8) ? syncevent4 : 0); - r >>= 4; - if (change_registered) - { - if (mRequestedRegistered && !mRequestedReady) - { - mRequestedRegistered = false; - this->unregister_client(); - } - } - else if (toggle_events) - { - mRequestedReady ^= toggle_events; - mRequestedRegistered = true; - this->ready(toggle_events, mRequestedReady & toggle_events); - } - llassert(mRequestedRegistered == is_registered()); - TestsuiteServerBase* server = this->server(); - llassert(!mRequestedRegistered || server->number_of_clients() == server->clients().size()); - if (mRequestedRegistered) - { - synceventset all_ready = syncevents; - synceventset any_ready = 0; - int nr = 0; - for (int cl = 0; cl < number_of_clients_per_syncgroup; ++cl) - { - if (syncgroup == syncgroup_test1) - { - if (client1p[cl].server() != server) - { - continue; - } - if (client1p[cl].getRequestedRegistered()) - { - ++nr; - all_ready &= client1p[cl].getRequestedReady(); - any_ready |= client1p[cl].getRequestedReady(); - } - } - else - { - if (client2p[cl].server() != server) - { - continue; - } - if (client2p[cl].getRequestedRegistered()) - { - ++nr; - all_ready &= client2p[cl].getRequestedReady(); - any_ready |= client2p[cl].getRequestedReady(); - } - } - } - llassert(nr == server->number_of_clients()); - llassert(!!(all_ready & syncevent1) == mActualReady1); - llassert(this->server()->events_with_all_clients_ready() == all_ready); - llassert(this->server()->events_with_at_least_one_client_ready() == any_ready); - llassert(nr == 0 || (any_ready & all_ready) == all_ready); - } - llassert(mRequestedReady == this->mReadyEvents); -} - -int main() -{ - Debug(libcw_do.on()); - Debug(dc::notice.on()); - Debug(libcw_do.set_ostream(&std::cout)); - Debug(list_channels_on(libcw_do)); - - unsigned short seed16v[3] = { 0x1234, 0xfedc, 0x7091 }; - - for (int k = 0;; ++k) - { - std::cout << "Loop: " << k << "; SEED: " << std::hex << seed16v[0] << ", " << seed16v[1] << ", " << seed16v[2] << std::dec << std::endl; - ++LLFrameTimer::sFrameCount; - - seed48(seed16v); - seed16v[0] = lrand48() & 0xffff; - seed16v[1] = lrand48() & 0xffff; - seed16v[2] = lrand48() & 0xffff; - - TestsuiteClient client1[number_of_clients_per_syncgroup]; - TestsuiteClient client2[number_of_clients_per_syncgroup]; - client1p = client1; - client2p = client2; - - for (int i = 0; i < number_of_clients_per_syncgroup; ++i) - { - client1[i].setIndex(i); - client2[i].setIndex(i); - } - - for (int j = 0; j < 1000000; ++j) - { - innerloop_count += 1; - -#ifdef DEBUG_SYNCOUTPUT - Dout(dc::notice, "Innerloop: " << j); -#endif - unsigned long r = lrand48(); - syncgroups grp = (r & 1) ? syncgroup_test1 : syncgroup_test2; - r >>= 1; - int cl = (r & 255) % number_of_clients_per_syncgroup; - r >>= 8; - switch (grp) - { - case syncgroup_test1: - client1[cl].change_state(r); - break; - case syncgroup_test2: - client2[cl].change_state(r); - break; - } - } - } -} - -#endif diff --git a/indra/llcommon/aisyncclient.h b/indra/llcommon/aisyncclient.h deleted file mode 100644 index b6fcbd103..000000000 --- a/indra/llcommon/aisyncclient.h +++ /dev/null @@ -1,417 +0,0 @@ -/** - * @file aisyncclient.h - * @brief Declaration of AISyncClient. - * - * Copyright (c) 2013, Aleric Inglewood. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution. - * - * CHANGELOG - * and additional copyright holders. - * - * 05/12/2013 - * Initial version, written by Aleric Inglewood @ SL - */ - -#ifndef AI_SYNC_CLIENT_H -#define AI_SYNC_CLIENT_H - -#ifdef SYNC_TESTSUITE -/* - * To compile the testsuite, run: - * - * cd indra/llcommon - * g++ -O3 -DCWDEBUG -DSYNC_TESTSUITE -I. -I../cwdebug aisyncclient.cpp -lcwd - */ - -#include -#include -typedef uint32_t U32; -typedef uint64_t U64; -#define LL_COMMON_API -#define SHOW_ASSERT -#define ASSERT_ONLY_COMMA(...) , __VA_ARGS__ -#define llassert assert - -struct LLFrameTimer -{ - double mStartTime; - double mExpiry; - static double getCurrentTime(void); - static U32 sFrameCount; - static U32 getFrameCount() { return sFrameCount; } - void reset(double expiration) { mStartTime = getCurrentTime(); mExpiry = mStartTime + expiration; } - bool hasExpired(void) const { return getCurrentTime() > mExpiry; } -}; - -#else // !SYNC_TESTSUITE -#include "llpreprocessor.h" -#include "stdtypes.h" -#include "llerror.h" -#include "llframetimer.h" -#endif -#include -#include - -class AISyncServer; - -/* - * AISyncClient - * - * This class allows to synchronize events between multiple client objects, - * derived from AISyncClient, where AISYNCSERVER must be - * derived from AISyncServer. - * - * Client objects call ready(mask) (or ready(event, bool), where each bit can be - * set or unset, which will result in alternating calls to event1_ready(mask) and - * event1_not_ready(mask) for all registered clients at the same time depending on - * when all clients are ready for syncevent1 or not (the other events can be - * polled, see below). - * - * For example, let a and b be two clients: - * - * { - * AISyncClient a; - * { - * AISyncClient b; - * - * a.ready(syncevent1, true); // Calls a.event1_ready(). - * a.ready(syncevent1, false); // Calls a.event1_not_ready(). - * b.ready(syncevent1, true); // Nothing happens. - * b.ready(syncevent1, false); // Nothing happens. - * b.ready(syncevent1, true); // Nothing happens. - * a.ready(syncevent1, true); // Calls a.event1_ready() and b.event1_ready(2). - * b.ready(syncevent1, false); // Calls a.event1_not_ready() and b.event1_not_ready(). - * } // Calls a.event1_ready() because b was destructed and a is still ready. - * a.ready(syncevent1, false); // Calls a.event1_not_ready(). Must set not-ready before destructing. - * } - * - * Clients can access the server by calling server() and poll it: - * - * AISyncServer* server = server(); - * - * if (server) - * { - * int n = server->number_of_clients(); - * synceventset set1 = server->events_with_all_clients_ready(); - * synceventset set2 = server->events_with_zero_clients_ready(); - * synceventset set3 = server->events_with_at_least_one_client_ready(); - * if (n == 0) - * { - * llassert(set1 == syncevents); // We define a server with no clients to have all clients ready. - * llassert(set2 == syncevents); // If there are no clients then every event has zero ready clients. - * llassert(set3 == 0); // If there are no clients then obviously set3 is the empty set. - * } - * else - * { - * llassert((set3 & set1) == set1); // set1 is a subset of set3. - * } - * llassert((set3 & (set1 & ~set2)) == (set1 & ~set2)); - * // The set of events that have all clients ready, but not zero clients, - * // is a subset of the set of events with at least one client ready. - * llassert((set2 ^ set3) == syncevents); // Each event belongs to either set2 or set3. - * } - * - * Clients can also unregister themselves without destruction, by calling unregister(); - * This might cause a call to event1_ready() for the remaining clients, if all of them - * are ready for syncevent1. - * - * Clients must be set not-ready for all events before unregistering them. - * - * Finally, it is possible to cause a server-side unregister for all clients, - * by calling server->dissolve(). This will cause an immediate call to event1_ready() - * for all clients that are ready, unless all were already ready. - */ - -enum syncgroups -{ -#ifdef SYNC_TESTSUITE - syncgroup_test1, - syncgroup_test2, -#else - syncgroup_motions, // Syncgroup used for animations. -#endif - syncgroup_size -}; - -typedef U32 sync_canonical_type; -typedef U64 sync_key_hash_type; - -int const sync_canonical_type_bits = sizeof(sync_canonical_type) * 8; -int const sync_number_of_events = 4; -int const sync_bits_per_event_counter = sync_canonical_type_bits / (sync_number_of_events + 1); - -// Each type exists of 4 event bytes. -typedef sync_canonical_type syncevent; // A single event: the least significant bit in one of the bytes. -typedef sync_canonical_type synceventset; // Zero or more events: the least significant bit in zero or more of the bytes. -typedef sync_canonical_type synccount; // A count [0, 255] in each of the bytes. -typedef sync_canonical_type synccountmask; // A single count mask: all eight bits set in one of the bytes. -typedef sync_canonical_type synccountmaskset; // Zero or more count masks: all eight bits set in zero or more of the bytes. - -syncevent const syncevent1 = 1; -syncevent const syncevent2 = syncevent1 << sync_bits_per_event_counter; -syncevent const syncevent3 = syncevent2 << sync_bits_per_event_counter; -syncevent const syncevent4 = syncevent3 << sync_bits_per_event_counter; -sync_canonical_type const syncrefcountunit = syncevent4 << sync_bits_per_event_counter; - -synccountmask const synccountmask1 = syncevent2 - 1; -synccountmask const synccountmask2 = synccountmask1 << sync_bits_per_event_counter; -synccountmask const synccountmask3 = synccountmask2 << sync_bits_per_event_counter; -synccountmask const synccountmask4 = synccountmask3 << sync_bits_per_event_counter; -synccountmask const syncrefcountmask = ((synccountmask)-1) & ~(syncrefcountunit - 1); - -synceventset const syncevents = syncevent1 | syncevent2 | syncevent3 | syncevent4; -synccountmask const syncoverflowbits = (syncevents << (sync_bits_per_event_counter - 1)) | ~(~(sync_canonical_type)0 >> 1); - -// Convert an event set into its corresponding count mask set. -inline synccountmaskset synceventset2countmaskset(synceventset events) -{ - synccountmaskset tmp1 = events << sync_bits_per_event_counter; - return (tmp1 & ~events) - (events & ~tmp1); -} - -// Convert a count mask set into an event set. -inline synceventset synccountmask2eventset(synccountmask mask) -{ - return mask & syncevents; -} - -// Interface class used to determined if a client and a server fit together. -class LL_COMMON_API AISyncKey : private LLFrameTimer -{ - private: - sync_key_hash_type mHash; - U32 mFrameCount; - - public: - // A static hash used for clients with no specific requirement. - static sync_key_hash_type const sNoRequirementHash; - - // A sync key object that returns the sNoRequirementHash. - static AISyncKey const sNoRequirement; - - public: - AISyncKey(sync_key_hash_type hash) : mHash(hash) { mFrameCount = getFrameCount(); reset(0.1); } - virtual ~AISyncKey() { } - - // Derived class should return a hash that is the same for clients which - // should be synchronized (assuming they meet the time slot requirement as well). - // The hash does not need to depend on on clock or frame number thus. - virtual sync_key_hash_type hash(void) const { return sNoRequirementHash; } - - // Return true if this key expired. - bool expired(void) const { return getFrameCount() > mFrameCount + 1 || hasExpired(); } - - // Return true if both keys are close enough to warrant synchronization. - bool matches(AISyncKey const& key) const { return key.mHash == mHash; } -}; - -class LL_COMMON_API AISyncClient_ServerPtr -{ - protected: - friend class AISyncServer; - boost::intrusive_ptr mSyncServer; - virtual ~AISyncClient_ServerPtr() { } - - // All-ready event. - virtual void event1_ready(void) = 0; // Called when, or after ready(), with the syncevent1 bit set, is called. Never called after a call to ready() with that bit unset. - // Not all-ready event. - virtual void event1_not_ready(void) = 0; // A call to event1_not_ready() follows (at most once) the call to event1_ready(), at some unspecified moment. - // Only client. Client was forcefully deregistered from expired server because it was the only client. - virtual void deregistered(void) - { -#ifdef SHOW_ASSERT - mReadyEvents = 0; -#endif - } - - // Derived classes must return a hash that determines whether or not clients can be synchronized (should be synchronized when registered at the same time). - virtual sync_key_hash_type sync_key_hash(void) const = 0; - -#ifdef SHOW_ASSERT - AISyncClient_ServerPtr(void) : mReadyEvents(0) { } - - protected: - synceventset mReadyEvents; // Used by AISyncServer for debugging only. -#endif -}; - -// AISyncServer -// -// This class represents a single group of AISyncClient's (a list of pointers to -// their AISyncClient_ServerPtr base class) that need to synchronize events. -// -class LL_COMMON_API AISyncServer -{ - public: - typedef std::list client_list_type; - - protected: - synccount mNrClients; // Four times (once in each byte) the number of registered clients (the size of mClients). - synccount mNrReady; // The number of clients that are ready (four counts, one for each syncevent). - client_list_type mClients; // The registered clients. - AISyncKey mSyncKey; // The characteristic of clients belonging to this synchronization server. - - public: - AISyncServer(AISyncKey const& sync_key) : mNrClients(0), mNrReady(0), mSyncKey(sync_key) { } - virtual ~AISyncServer() { } - - // Default creation of a server for 'syncgroup'. - template - static void create_server(boost::intrusive_ptr& server, AISyncKey const& sync_key); - // Default removal of a server for 'syncgroup'. - template - static void dispose_server(boost::intrusive_ptr& server); - - // Called from AISyncServerCache. - void setSyncKey(AISyncKey const& sync_key) { mSyncKey = sync_key; } - - // Register client with a server from group syncgroup. - template - static void register_client(AISyncClient_ServerPtr* client); - // Unregister the client (syncgroup must be the same as what was passed to register it). - void unregister_client(AISyncClient_ServerPtr* client, syncgroups syncgroup); - - // Set readiness of all events at once. - bool ready(synceventset events, synceventset yesno/*,*/ ASSERT_ONLY_COMMA(AISyncClient_ServerPtr* client)); - // One event became ready or not ready. - bool ready(syncevent event, bool yesno/*,*/ ASSERT_ONLY_COMMA(AISyncClient_ServerPtr* client)) { return ready(event, yesno ? event : 0/*,*/ ASSERT_ONLY_COMMA(client)); } - - // Returns bitwise OR of syncevent1 through syncevent4 for - // events for which all clients are ready. - synceventset events_with_all_clients_ready(void) const; - // events for which at least one client is ready. - synceventset events_with_at_least_one_client_ready(void) const; - // events for which zero clients are ready. - synceventset events_with_zero_clients_ready(void) const { return syncevents ^ events_with_at_least_one_client_ready(); } - - // Return the number registered clients. - int number_of_clients(void) const { return (int)(mNrClients & synccountmask1); } - int get_refcount(void) const { return (int)(mNrClients >> (sync_number_of_events * sync_bits_per_event_counter)); } - - private: - void trigger_ready(void); // Calls event1_ready() for all clients. - void trigger_not_ready(void); // Calls event1_not_ready for all clients. - - void unregister_last_client(void); - - friend void intrusive_ptr_add_ref(AISyncServer* server); - friend void intrusive_ptr_release(AISyncServer* server); -}; - -template -class LL_COMMON_API AISyncClient_SyncGroup : public AISyncClient_ServerPtr -{ - public: - // Call 'ready' when you are ready (or not) to get a call to start(). - // Returns true if that call was made (immediately), otherwise it may happen later. - - bool ready(synceventset events, synceventset yesno) // Set readiness of all events at once. - { - if (!mSyncServer) - { - AISyncServer::register_client(this); - } - return mSyncServer->ready(events, yesno/*,*/ ASSERT_ONLY_COMMA(this)); - } - bool ready(syncevent event, bool yesno = true) // One event became ready or not ready. - { - if (!mSyncServer) - { - AISyncServer::register_client(this); - } - return mSyncServer->ready(event, yesno/*,*/ ASSERT_ONLY_COMMA(this)); - } - - void unregister_client(void) - { - if (mSyncServer) - { - mSyncServer->unregister_client(this, syncgroup); - } - } - - protected: - virtual ~AISyncClient_SyncGroup() { unregister_client(); } -}; - -template -class LL_COMMON_API AISyncClient : public AISyncClient_SyncGroup -{ - public: - AISYNCSERVER* server(void) { return static_cast(this->mSyncServer.get()); } - AISYNCSERVER const* server(void) const { return static_cast(this->mSyncServer.get()); } - - // By default return a general sync key that will cause synchronizing solely based on time. - /*virtual*/ AISyncKey const& getSyncKey(void) const { return AISyncKey::sNoRequirement; } -}; - -template -class AISyncServerCache -{ - private: - // The server cache exists of a few pointers to unused server instances. - // The cache is empty when sSize == 0 and full when sSize == 16; - static int const cache_size = 16; - static boost::intrusive_ptr sServerCache[cache_size]; - static int sSize; - - public: - static void create_server(boost::intrusive_ptr& server, AISyncKey const& sync_key) - { - if (sSize == 0) // Cache empty? - { - server.reset(new AISYNCSERVER(sync_key)); - } - else - { - sServerCache[--sSize].swap(server); - server->setSyncKey(sync_key); - } - } - - static void dispose_server(boost::intrusive_ptr& server) - { - if (sSize < cache_size) - { - sServerCache[sSize++].swap(server); - } - server.reset(); - } -}; - -template -boost::intrusive_ptr AISyncServerCache::sServerCache[cache_size]; - -template -int AISyncServerCache::sSize; - -template -inline void AISyncServer::create_server(boost::intrusive_ptr& server, AISyncKey const& sync_key) -{ - AISyncServerCache::create_server(server, sync_key); -} - -template -inline void AISyncServer::dispose_server(boost::intrusive_ptr& server) -{ - AISyncServerCache::dispose_server(server); -} - -#endif // AI_SYNC_CLIENT_H -