diff --git a/src/hotspot/share/gc/shared/workgroup.cpp b/src/hotspot/share/gc/shared/workgroup.cpp index 54da23b..7c4a614 100644 --- a/src/hotspot/share/gc/shared/workgroup.cpp +++ b/src/hotspot/share/gc/shared/workgroup.cpp @@ -32,6 +32,7 @@ #include "runtime/os.hpp" #include "runtime/semaphore.hpp" #include "runtime/thread.inline.hpp" +#include "utilities/behaviours.hpp" // Definitions of WorkGang methods. @@ -137,6 +138,7 @@ public: void coordinator_execute_on_workers(AbstractGangTask* task, uint num_workers) { // No workers are allowed to read the state variables until they have been signaled. + task->set_provider(Behaviours::get_provider()); _task = task; _not_finished = num_workers; @@ -201,6 +203,7 @@ class MutexGangTaskDispatcher : public GangTaskDispatcher { void coordinator_execute_on_workers(AbstractGangTask* task, uint num_workers) { MutexLockerEx ml(_monitor, Mutex::_no_safepoint_check_flag); + task->set_provider(Behaviours::get_provider()); _task = task; _num_workers = num_workers; @@ -330,6 +333,7 @@ void GangWorker::signal_task_done() { } void GangWorker::run_task(WorkData data) { + ForwardingProviderMark behaviour_provider_mark(data._task->provider()); GCIdMark gc_id_mark(data._task->gc_id()); log_develop_trace(gc, workgang)("Running work gang: %s task: %s worker: %u", name(), data._task->name(), data._worker_id); diff --git a/src/hotspot/share/gc/shared/workgroup.hpp b/src/hotspot/share/gc/shared/workgroup.hpp index 9efa136..5c1edcf 100644 --- a/src/hotspot/share/gc/shared/workgroup.hpp +++ b/src/hotspot/share/gc/shared/workgroup.hpp @@ -55,11 +55,13 @@ class WorkGang; // An abstract task to be worked on by a gang. // You subclass this to supply your own work() method class AbstractGangTask { + BehaviourProvider* _provider; const char* _name; const uint _gc_id; public: explicit AbstractGangTask(const char* name) : + _provider(NULL), _name(name), _gc_id(GCId::current_or_undefined()) {} @@ -71,12 +73,18 @@ class AbstractGangTask { // Debugging accessor for the name. const char* name() const { return _name; } const uint gc_id() const { return _gc_id; } + BehaviourProvider& provider() const { + assert(_provider != NULL, "no provider set"); + return *_provider; + } + void set_provider(BehaviourProvider& provider) { _provider = &provider; } }; struct WorkData { - AbstractGangTask* _task; - uint _worker_id; - WorkData(AbstractGangTask* task, uint worker_id) : _task(task), _worker_id(worker_id) {} + AbstractGangTask* _task; + uint _worker_id; + WorkData(AbstractGangTask* task, uint worker_id) + : _task(task), _worker_id(worker_id) {} }; // Interface to handle the synchronization between the coordinator thread and the worker threads, diff --git a/src/hotspot/share/memory/universe.cpp b/src/hotspot/share/memory/universe.cpp index 7deb13b..3ed3d00 100644 --- a/src/hotspot/share/memory/universe.cpp +++ b/src/hotspot/share/memory/universe.cpp @@ -73,6 +73,7 @@ #include "runtime/vm_operations.hpp" #include "services/memoryService.hpp" #include "utilities/align.hpp" +#include "utilities/behaviours.hpp" #include "utilities/copy.hpp" #include "utilities/debug.hpp" #include "utilities/events.hpp" @@ -667,6 +668,17 @@ void* Universe::non_oop_word() { return (void*)_non_oop_bits; } +BehaviourProviderCollection* Universe::_vm_behaviours = NULL; +BehaviourProviderCollection* Universe::_gc_behaviours = NULL; + +void Universe::initialize_global_behaviours() { + // During VM bootstrap + _vm_behaviours = new BehaviourProviderCollection(); + _gc_behaviours = new BehaviourProviderCollection(); + Behaviours::register_global_provider(*_vm_behaviours); + Behaviours::register_global_provider(*_gc_behaviours); +} + jint universe_init() { assert(!Universe::_fully_initialized, "called after initialize_vtables"); guarantee(1 << LogHeapWordSize == sizeof(HeapWord), @@ -679,6 +691,8 @@ jint universe_init() { JavaClasses::compute_hard_coded_offsets(); + Universe::initialize_global_behaviours(); + jint status = Universe::initialize_heap(); if (status != JNI_OK) { return status; diff --git a/src/hotspot/share/memory/universe.hpp b/src/hotspot/share/memory/universe.hpp index f7fd4a1..6b230c7 100644 --- a/src/hotspot/share/memory/universe.hpp +++ b/src/hotspot/share/memory/universe.hpp @@ -37,6 +37,7 @@ // support is provided. Allocation by the interpreter and compiled code is done inline // and bails out to Scavenge::invoke_and_allocate. +class BehaviourProviderCollection; class CollectedHeap; class DeferredObjAllocEvent; @@ -209,6 +210,10 @@ class Universe: AllStatic { static bool _module_initialized; // true after call_initPhase2 called static bool _fully_initialized; // true after universe_init and initialize_vtables called + // Global behaviours + static BehaviourProviderCollection* _vm_behaviours; + static BehaviourProviderCollection* _gc_behaviours; + // the array of preallocated errors with backtraces static objArrayOop preallocated_out_of_memory_errors() { return _preallocated_out_of_memory_error_array; } @@ -220,6 +225,8 @@ class Universe: AllStatic { static size_t _heap_capacity_at_last_gc; static size_t _heap_used_at_last_gc; + static void initialize_global_behaviours(); + static CollectedHeap* create_heap(); static jint initialize_heap(); static void initialize_basic_type_mirrors(TRAPS); @@ -387,6 +394,9 @@ class Universe: AllStatic { // The particular choice of collected heap. static CollectedHeap* heap() { return _collectedHeap; } + static BehaviourProviderCollection* vm_behaviours() { return _vm_behaviours; } + static BehaviourProviderCollection* gc_behaviours() { return _gc_behaviours; } + // For UseCompressedOops // Narrow Oop encoding mode: // 0 - Use 32-bits oops without encoding when diff --git a/src/hotspot/share/runtime/thread.cpp b/src/hotspot/share/runtime/thread.cpp index 45b19a4..2bb6044 100644 --- a/src/hotspot/share/runtime/thread.cpp +++ b/src/hotspot/share/runtime/thread.cpp @@ -275,6 +275,8 @@ Thread::Thread() { Monitor::_safepoint_check_sometimes); _suspend_flags = 0; + _behaviour_provider = NULL; + // thread-specific hashCode stream generator state - Marsaglia shift-xor form _hashStateX = os::random(); _hashStateY = 842502087; diff --git a/src/hotspot/share/runtime/thread.hpp b/src/hotspot/share/runtime/thread.hpp index 9e95a15..9a00bb7 100644 --- a/src/hotspot/share/runtime/thread.hpp +++ b/src/hotspot/share/runtime/thread.hpp @@ -56,6 +56,7 @@ #include "jfr/support/jfrThreadExtension.hpp" #endif +class BehaviourProvider; class SafeThreadsListPtr; class ThreadSafepointState; @@ -173,7 +174,12 @@ class Thread: public ThreadShadow { return _nested_threads_hazard_ptr_cnt; } + BehaviourProvider* _behaviour_provider; + public: + BehaviourProvider* behaviour_provider() const { return _behaviour_provider; } + void set_behaviour_provider(BehaviourProvider* behaviour_provider) { _behaviour_provider = behaviour_provider; } + void* operator new(size_t size) throw() { return allocate(size, true); } void* operator new(size_t size, const std::nothrow_t& nothrow_constant) throw() { return allocate(size, false); } diff --git a/src/hotspot/share/utilities/behaviours.cpp b/src/hotspot/share/utilities/behaviours.cpp new file mode 100644 index 0000000..2577669 --- /dev/null +++ b/src/hotspot/share/utilities/behaviours.cpp @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "precompiled.hpp" +#include "memory/allocation.inline.hpp" +#include "runtime/thread.hpp" +#include "utilities/behaviours.hpp" +#include "utilities/copy.hpp" +#include "utilities/debug.hpp" + +volatile int BehaviourRegistry::_behaviour_count = 0; +BehaviourProviderCollection* Behaviours::_global_provider = new BehaviourProviderCollection(); + +BehaviourProviderMark::BehaviourProviderMark(BehaviourProvider& provider) + : _attached_thread(Thread::current()), + _cache(&provider, _attached_thread), + _provider(provider), + _parent(&Behaviours::get_provider()) +{ + _attached_thread->set_behaviour_provider(&_cache); +} + +BehaviourProviderMark::~BehaviourProviderMark() { + _attached_thread->set_behaviour_provider(_provider.parent()); +} + +BehaviourProvider* BehaviourProvider::provider(Thread* current, int behaviour_id) { + assert(_parent != NULL, "could not find behaviour provider"); + BehaviourProvider* result = _parent->provider(current, behaviour_id); + assert(result != NULL, "no behaviour provider found"); + return result; +} + +BehaviourProviderHashCache::BehaviourProviderHashCache(BehaviourProvider* parent, Thread* attached_thread) + : BehaviourProvider(parent), + _cache(NULL), + _owner(attached_thread) +{ } + +BehaviourProviderHashCache::~BehaviourProviderHashCache() { + if (_cache != NULL) { + delete _cache; + } +} + +BehaviourProvider* BehaviourProviderHashCache::provider(Thread* current, int behaviour_id) { + if (_owner != current) { + return BehaviourProvider::provider(current, behaviour_id); + } + if (_cache == NULL) { + _cache = new (ResourceObj::C_HEAP, mtInternal) ProviderTable(); + } + + BehaviourProvider** result = _cache->get(behaviour_id); + if (result != NULL) { + return *result; + } + + _cache->put(behaviour_id, BehaviourProvider::provider(current, behaviour_id)); + return *_cache->get(behaviour_id); +} + +void* BehaviourProviderHashCache::behaviour(int behaviour_id) { + ShouldNotReachHere(); + return NULL; +} + +void BehaviourProviderCollection::prepend_node(BehaviourProviderNode* node) { + assert(node->provider().parent() == NULL, "invariant"); + + if (is_empty()) { + node->provider().set_parent(parent()); + _head = node; + _tail = node; + } else { + node->provider().set_parent(&_head->provider()); + _head = node; + } +} + +BehaviourProviderCollection::BehaviourProviderCollection() + : BehaviourProvider(NULL), + _head(NULL), + _tail(NULL) +{ } + +BehaviourProvider* BehaviourProviderCollection::provider(Thread* current, int behaviour_id) { + if (is_empty()) { + return BehaviourProvider::provider(current, behaviour_id); + } else { + return _head->provider().provider(current, behaviour_id); + } +} + +void* BehaviourProviderCollection::behaviour(int behaviour_id) { + ShouldNotReachHere(); + return NULL; +} + +void BehaviourProviderCollection::register_provider(BehaviourProvider& provider) { + BehaviourProviderNode* node = new BehaviourProviderNode(provider); + prepend_node(node); +} + +void BehaviourProviderCollection::set_parent(BehaviourProvider* parent) { + BehaviourProvider::set_parent(parent); + if (!is_empty()) { + _tail->provider().set_parent(parent); + } +} diff --git a/src/hotspot/share/utilities/behaviours.hpp b/src/hotspot/share/utilities/behaviours.hpp new file mode 100644 index 0000000..1aa3a60 --- /dev/null +++ b/src/hotspot/share/utilities/behaviours.hpp @@ -0,0 +1,297 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_UTILITIES_BEHAVIOURS_HPP +#define SHARE_UTILITIES_BEHAVIOURS_HPP + +#include "memory/allocation.hpp" +#include "runtime/atomic.hpp" +#include "runtime/thread.hpp" +#include "utilities/debug.hpp" +#include "utilities/resourceHash.hpp" + +// The BehaviourRegistry maps types to unique integers. +class BehaviourRegistry: AllStatic { + template + struct BehaviourIdentifier { + static int _behaviour_id; + }; + + template + friend struct BehaviourIdentifier; + static volatile int _behaviour_count; + +public: + template + static int get_behaviour_id() { + return BehaviourIdentifier::_behaviour_id; + } + + static int get_behaviour_count() { return _behaviour_count; } +}; + +template +int BehaviourRegistry::BehaviourIdentifier::_behaviour_id = + Atomic::add(1, &BehaviourRegistry::_behaviour_count) - 1; + +// A BehaviourProvider knows how to get the appropriate provider for a given +// behaviour type. +class BehaviourProvider { + friend class Behaviours; + BehaviourProvider* _parent; + +public: + BehaviourProvider(BehaviourProvider* parent) : _parent(parent) { } + + virtual BehaviourProvider* provider(Thread* current, int behaviour_id); + virtual void* behaviour(int behaviour_id) = 0; + BehaviourProvider* parent() const { return _parent; } + virtual void set_parent(BehaviourProvider* parent) { _parent = parent; } +}; + +// A hash cache provider makes a hash table containing quick lookup of +// providers for a given behaviour type. For each lookup, it caches +// the found provider for a given behaviour type so that the next lookup +// will be quick. +class BehaviourProviderHashCache: public BehaviourProvider { + typedef ResourceHashtable ProviderTable; + ProviderTable* _cache; + Thread* _owner; + +public: + BehaviourProviderHashCache(BehaviourProvider* parent, Thread* attached_thread); + ~BehaviourProviderHashCache(); + + virtual BehaviourProvider* provider(Thread* current, int behaviour_id); + virtual void* behaviour(int behaviour_id); +}; + +// A singleton behaviour provider provides a single behaviour and delegates +// all other requests to the parent provider. Chaining singleton providers +// is equivalent to creating a chain of responsibility, which allows layering +// behaviours in a structured way. +class SingletonBehaviourProvider: public BehaviourProvider { + void* _behaviour; + int _behaviour_id; + + bool provides_behaviour(int behaviour_id) { + return _behaviour_id == behaviour_id; + } + +public: + SingletonBehaviourProvider(BehaviourProvider* parent, void* behaviour, int behaviour_id) + : BehaviourProvider(parent), + _behaviour(behaviour), + _behaviour_id(behaviour_id) + { } + + SingletonBehaviourProvider(void* behaviour, int behaviour_id) + : BehaviourProvider(NULL), + _behaviour(behaviour), + _behaviour_id(behaviour_id) + { } + + virtual BehaviourProvider* provider(Thread* current, int behaviour_id) { + if (provides_behaviour(behaviour_id)) { + return this; + } else { + return BehaviourProvider::provider(current, behaviour_id); + } + } + + virtual void* behaviour(int behaviour_id) { + return _behaviour; + } +}; + +// A forwarding behaviour provider provides no behaviours, but delegates +// requests to the parent provider. This allows sending requests elsewhere. +class ForwardingBehaviourProvider: public BehaviourProvider { +public: + ForwardingBehaviourProvider(BehaviourProvider* parent) + : BehaviourProvider(parent) + { } + + virtual void* behaviour(int behaviour_id) { ShouldNotReachHere(); return NULL; } +}; + +// A behaviour collection allows registering multiple behaviour providers +// in a single collection of behaviours. This is useful when layering behaviours +// so that one layer overrides the haviour of a parent layer of behaviours. Then +// a user may simply add behaviours to the layer (comprised by a collection). +class BehaviourProviderCollection: public BehaviourProvider, public CHeapObj { + class BehaviourProviderNode: public CHeapObj { + BehaviourProvider& _provider; + + public: + BehaviourProviderNode(BehaviourProvider& provider) + : _provider(provider) + { } + + BehaviourProvider& provider() { + return _provider; + } + }; + + class SingletonBehaviourProviderNode: public BehaviourProviderNode { + SingletonBehaviourProvider _provider; + + public: + SingletonBehaviourProviderNode(void* behaviour, int behaviour_id) + : BehaviourProviderNode(_provider), + _provider(NULL, behaviour, behaviour_id) + { } + }; + + BehaviourProviderNode* _head; + BehaviourProviderNode* _tail; + + bool is_empty() const { + return _head == NULL && _tail == NULL; + } + + void prepend_node(BehaviourProviderNode* node); + +public: + BehaviourProviderCollection(); + + virtual void set_parent(BehaviourProvider* parent); + virtual BehaviourProvider* provider(Thread* current, int behaviour_id); + virtual void* behaviour(int behaviour_id); + void register_provider(BehaviourProvider& provider); + + template + void register_behaviour(T& behaviour) { + BehaviourProviderNode* node = new SingletonBehaviourProviderNode(&behaviour, + BehaviourRegistry::get_behaviour_id()); + prepend_node(node); + } +}; + +// This utility class is used to get the current provider for the current execution +// context, as well as getting the current behaviour for the current execution context. +class Behaviours: AllStatic { + static BehaviourProviderCollection* _global_provider; + +public: + static BehaviourProvider& get_provider(Thread* current = Thread::current()) { + BehaviourProvider* provider = current->behaviour_provider(); + if (provider == NULL) { + provider = _global_provider; + } + return *provider; + } + + template + static T& get_behaviour() { + Thread* current = Thread::current(); + BehaviourProvider* provider = &get_provider(); + int behaviour_id = BehaviourRegistry::get_behaviour_id(); + provider = provider->provider(current, behaviour_id); + void* behaviour = provider->behaviour(behaviour_id); + assert(behaviour != NULL, "did not find any provided behaviour"); + return *reinterpret_cast(behaviour); + } + + template + static T& get_super_behaviour(T* child) { + Thread* current = Thread::current(); + BehaviourProvider* provider = &get_provider(); + int behaviour_id = BehaviourRegistry::get_behaviour_id(); + provider = provider->provider(current, behaviour_id); + while (reinterpret_cast(provider->behaviour(behaviour_id)) != child) { + provider = provider->parent()->provider(current, behaviour_id); + } + provider = provider->parent()->provider(current, behaviour_id); + void* behaviour = provider->behaviour(behaviour_id); + assert(behaviour != NULL, "did not find any provided behaviour"); + T* result = reinterpret_cast(behaviour); + assert(result != child, "sanity"); + return *result; + } + + static void register_global_provider(BehaviourProvider& provider) { + _global_provider->register_provider(provider); + } + + static BehaviourProvider& global_provider() { + return *_global_provider; + } +}; + +class BehaviourProviderMark: StackObj { +protected: + Thread* _attached_thread; + BehaviourProviderHashCache _cache; + BehaviourProvider& _provider; + BehaviourProvider* _parent; + +public: + BehaviourProviderMark(BehaviourProvider& provider); + ~BehaviourProviderMark(); +}; + +// The BehaviourMark is used to provide a behaviour in a local scope, layering it +// above the current execution context. +template +class BehaviourMark: public BehaviourProviderMark { + SingletonBehaviourProvider _provider; + +public: + BehaviourMark(T& behaviour) + : BehaviourProviderMark(_provider), + _provider(_parent, + &behaviour, + BehaviourRegistry::get_behaviour_id()) + { } +}; + +// The ForwardingProviderMark is used to forward a provider into a local scope, layering it +// above the current execution context. +class ForwardingProviderMark: public BehaviourProviderMark { + ForwardingBehaviourProvider _provider; + +public: + ForwardingProviderMark(BehaviourProvider& provider) + : BehaviourProviderMark(_provider), + _provider(&provider) + { } +}; + +// The DefaultBehaviourMark is similar to the BehaviourMark, but also contains the +// actual behaviour as part of the sack allocated object, created with the default +// constructor for that type. +template +class DefaultBehaviourMark: public BehaviourMark { + ConcreteT _behaviour; + +public: + DefaultBehaviourMark() + : BehaviourMark(_behaviour) + { } +}; + +#endif // SHARE_UTILITIES_BEHAVIOURS_HPP diff --git a/test/hotspot/gtest/utilities/test_behaviours.cpp b/test/hotspot/gtest/utilities/test_behaviours.cpp new file mode 100644 index 0000000..6f404e3 --- /dev/null +++ b/test/hotspot/gtest/utilities/test_behaviours.cpp @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + + */ + +#include "precompiled.hpp" +#include "unittest.hpp" +#include "utilities/behaviours.hpp" + +class ReturnNumberBehaviour { +public: + virtual int number() = 0; +}; + +class ReturnFiveBehaviour: public ReturnNumberBehaviour { +public: + virtual int number() { + return 5; + } +}; + +class ReturnSixBehaviour: public ReturnNumberBehaviour { +public: + virtual int number() { + return 6; + } +}; + +class ReturnOneBehaviour: public ReturnNumberBehaviour { +public: + virtual int number() { + return 1; + } +}; + +TEST(Behaviours, local) { + DefaultBehaviourMark bm; + ASSERT_EQ(Behaviours::get_behaviour().number(), 5) << "Should be 5"; +} + +TEST(Behaviours, local_stacked) { + DefaultBehaviourMark bm; + DefaultBehaviourMark bm2; + ASSERT_EQ(Behaviours::get_behaviour().number(), 6) << "Should be 6"; +} + +TEST(Behaviours, global) { + BehaviourProviderCollection providers; + Behaviours::register_global_provider(providers); + + ReturnFiveBehaviour b1; + ReturnSixBehaviour b2; + + providers.register_behaviour(b1); + providers.register_behaviour(b2); + + ASSERT_EQ(Behaviours::get_behaviour().number(), 6) << "Should be 6"; + + { + DefaultBehaviourMark bm; + ASSERT_EQ(Behaviours::get_behaviour().number(), 1) << "Should be 1"; + ASSERT_EQ(Behaviours::get_behaviour().number(), 1) << "Should be 1"; + + { + DefaultBehaviourMark bm; + ASSERT_EQ(Behaviours::get_behaviour().number(), 5) << "Should be 5"; + ASSERT_EQ(Behaviours::get_behaviour().number(), 5) << "Should be 5"; + } + + ASSERT_EQ(Behaviours::get_behaviour().number(), 1) << "Should be 1"; + ASSERT_EQ(Behaviours::get_behaviour().number(), 1) << "Should be 1"; + } + + ASSERT_EQ(Behaviours::get_behaviour().number(), 6) << "Should be 6"; +}