blob: f368ac96624983dc386dc6aec1c496cbf15bafb0 [file] [log] [blame]
/*
* 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.
*/
// Thread-synchronization utility class for reproducing races in unit tests.
#ifndef PAGESPEED_KERNEL_THREAD_THREAD_SYNCHRONIZER_H__
#define PAGESPEED_KERNEL_THREAD_THREAD_SYNCHRONIZER_H__
#include <map>
#include "pagespeed/kernel/base/basictypes.h"
#include "pagespeed/kernel/base/scoped_ptr.h"
#include "pagespeed/kernel/base/string.h"
#include "pagespeed/kernel/base/string_util.h"
namespace net_instaweb {
class AbstractMutex;
class ThreadSystem;
class Timer;
// Helps create deterministic multi-threaded tests targeting
// programmer-identified race conditions.
//
// Note that the goal of this class is not to provoke, or test for
// potential race conditions. That is a noble goal, left for another
// day.
//
// The goal of this class is to help programmers that suspect they have
// found a race condition reproduce it robustly in a way that can be
// checked in as a unit-test and run instantly.
//
// This methodology does not rely on sleep(). It uses condition
// variables so it will run as fast as possible. The sync-points can
// be left in all but the most nano-second-optimized production code
// as they are no-ops (single inlined if-statement) when turned off.
//
// This class is disabled by default, so that calls to it can be left in
// production code, but enabled for targeted tests.
class ThreadSynchronizer {
public:
explicit ThreadSynchronizer(ThreadSystem* thread_system);
~ThreadSynchronizer();
// By default, the synchronizer is a no-op so we can inject sync-points
// in production code at sensitive points with minimal overhead. To
// enable in a test, call EnableForPrefix("Prefix"), which will enable
// any synchronization key beginning with "Prefix".
//
// EnableForPrefix should be called prior to any threading.
void EnableForPrefix(StringPiece prefix) {
enabled_ = true;
prefix.CopyToString(StringVectorAdd(&prefixes_));
}
// Waits for a thread to signal the specified key.
void Wait(const char* key) {
if (enabled_) {
DoWait(key);
}
}
// Waits for a thread to signal the specified key, or the specified timeout in
// milliseconds, whichever comes first. Note that even if the timer expires,
// the program should eventually Signal the key unless AllowSloppyTermination
// is called.
void TimedWait(const char* key, int64 timeout_ms) {
if (enabled_) {
DoTimedWait(key, timeout_ms);
}
}
// Signals any thread waiting for a key that it can continue. Signals
// delivered in advance of a wait are remembered. It is an error to
// destruct the ThreadSynchronizer with pending signals, unless
// AllowSloppyTermination is called for the key.
void Signal(const char* key) {
if (enabled_) {
DoSignal(key);
}
}
// Signals that are delivered in a timing-dependent fashion may not
// be totally balanced at the end of a test. Such signals should be
// the exception rather than the rule, but they can be declared with
// this method. This method is intended to be called from tests
// only, thus it does not have an inline shunt.
void AllowSloppyTermination(const char* key);
private:
class SyncPoint;
typedef std::map<GoogleString, SyncPoint*> SyncMap;
SyncPoint* GetSyncPoint(const GoogleString& key);
void DoWait(const char* key);
void DoTimedWait(const char* key, int64 timeout_ms);
void DoSignal(const char* key);
bool MatchesPrefix(const char* key) const;
bool enabled_;
ThreadSystem* thread_system_;
SyncMap sync_map_;
scoped_ptr<AbstractMutex> map_mutex_;
scoped_ptr<Timer> timer_;
StringVector prefixes_;
DISALLOW_COPY_AND_ASSIGN(ThreadSynchronizer);
};
} // namespace net_instaweb
#endif // PAGESPEED_KERNEL_THREAD_THREAD_SYNCHRONIZER_H__