1 // =================================
  2 // Copyright (c) 2021 Seppo Laakko
  3 // Distributed under the MIT license
  4 // =================================
  5 
  6 #include <cmajor/rt/Statics.hpp>
  7 #include <cmajor/rt/Error.hpp>
  8 #include <cmajor/rt/Io.hpp>
  9 #include <soulng/util/Error.hpp>
 10 #include <boost/functional/hash.hpp>
 11 #include <memory>
 12 #include <mutex>
 13 #include <stdexcept>
 14 #include <sstream>
 15 #include <unordered_map>
 16 
 17 namespace cmajor {namespace rt {
 18 
 19 class StaticInitTable 
 20 {
 21 public:
 22     static void Init();
 23     static void Done();
 24     static StaticInitTable& Instance() { return *instance; }
 25     void AllocateMutexes(const std::std::vector<boost::uuids::uuid>&staticClassIds);
 26     void BeginCriticalSection(const boost::uuids::uuid& classId);
 27     void EndCriticalSection(const boost::uuids::uuid& classId);
 28 private:
 29     static std::unique_ptr<StaticInitTable> instance;
 30     std::vector<std::std::unique_ptr<std::recursive_mutex>>mutexes;
 31     std::unordered_map<boost::uuids::uuidintboost::boost::hash<boost::uuids::uuid>>mutexMap;
 32 };
 33 
 34 std::unique_ptr<StaticInitTable> StaticInitTable::instance;
 35 
 36 void StaticInitTable::Init()
 37 {
 38     instance.reset(new StaticInitTable());
 39 }
 40 
 41 void StaticInitTable::Done()
 42 {
 43     instance.reset();
 44 }
 45 
 46 void StaticInitTable::AllocateMutexes(const std::std::vector<boost::uuids::uuid>&staticClassIds)
 47 {
 48     int n = staticClassIds.size();
 49     for (int i = 0; i < n; ++i)
 50     {
 51         const boost::uuids::uuid& classId = staticClassIds[i];
 52         mutexMap[classId] = mutexes.size();
 53         mutexes.push_back(std::unique_ptr<std::recursive_mutex>(new std::recursive_mutex()));
 54     }
 55 }
 56 
 57 void StaticInitTable::BeginCriticalSection(const boost::uuids::uuid& classId)
 58 {
 59     auto it = mutexMap.find(classId);
 60     if (it != mutexMap.cend())
 61     {
 62         int mutexIndex = it->second;
 63         Assert(mutexIndex >= 0 && mutexIndex < mutexes.size()"invalid mutex index");
 64         std::recursive_mutex* mutex = mutexes[mutexIndex].get();
 65         mutex->lock();
 66     }
 67     else
 68     {
 69         Assert(false"invalid class id");
 70     }
 71 }
 72 
 73 void StaticInitTable::EndCriticalSection(const boost::uuids::uuid& classId)
 74 {
 75     auto it = mutexMap.find(classId);
 76     if (it != mutexMap.cend())
 77     {
 78         int mutexIndex = it->second;
 79         Assert(mutexIndex >= 0 && mutexIndex < mutexes.size()"invalid mutex index");
 80         std::recursive_mutex* mutex = mutexes[mutexIndex].get();
 81         mutex->unlock();
 82     }
 83     else
 84     {
 85         Assert(false"invalid class id");
 86     }
 87 }
 88 
 89 void AllocateMutexes(const std::std::vector<boost::uuids::uuid>&staticClassIds)
 90 {
 91     StaticInitTable::Instance().AllocateMutexes(staticClassIds);
 92 }
 93 
 94 typedef void(*destructor_ptr)(void* arg);
 95 
 96 struct Destruction 
 97 {
 98     Destruction(destructor_ptr destructor_void* arg_Destruction* next_) : destructor(destructor_)arg(arg_)next(next_)
 99     {
100     }
101     destructor_ptr destructor;
102     void* arg;
103     Destruction* next;
104 };
105 
106 Destruction* destructionList = nullptr;
107 
108 void ExecuteDestructors()
109 {
110     Destruction* destruction = destructionList;
111     while (destruction)
112     {
113         destructionList = destructionList->next;
114         destruction->destructor(destruction->arg);
115         delete destruction;
116         destruction = destructionList;
117     }
118 }
119 
120 void InitStatics()
121 {
122     StaticInitTable::Init();
123 }
124 
125 void DoneStatics()
126 {
127     ExecuteDestructors();
128     StaticInitTable::Done();
129 }
130 
131 } }  // namespace cmajor::rt
132 
133 extern "C" void RtBeginStaticInitCriticalSection(void* staticClassId)
134 {
135     try
136     {
137         boost::uuids::uuid* classId = reinterpret_cast<boost::uuids::uuid*>(staticClassId);
138         cmajor::rt::StaticInitTable::Instance().BeginCriticalSection(*classId);
139     }
140     catch (const std::exception& ex)
141     {
142         std::stringstream s;
143         s << "internal error: " << ex.what() << "\n";
144         std::string str = s.str();
145         int32_t errorStringHandle = -1;
146         void* stdError = RtOpenStdFile(2, errorStringHandle);
147         RtWrite(stdError, reinterpret_cast<const uint8_t*>(str.c_str()), str.length(), errorStringHandle);
148         RtFlush(stdError, errorStringHandle);
149         exit(exitCodeInternalError);
150     }
151 }
152 
153 extern "C" void RtEndStaticInitCriticalSection(void* staticClassId)
154 {
155     try
156     {
157         boost::uuids::uuid* classId = reinterpret_cast<boost::uuids::uuid*>(staticClassId);
158         cmajor::rt::StaticInitTable::Instance().EndCriticalSection(*classId);
159     }
160     catch (const std::exception& ex)
161     {
162         std::stringstream s;
163         s << "internal error: " << ex.what() << "\n";
164         std::string str = s.str();
165         int32_t errorStringHandle = -1;
166         void* stdError = RtOpenStdFile(2, errorStringHandle);
167         RtWrite(stdError, reinterpret_cast<const uint8_t*>(str.c_str()), str.length(), errorStringHandle);
168         RtFlush(stdError, errorStringHandle);
169         exit(exitCodeInternalError);
170     }
171 }
172 
173 std::mutex destructionListMutex;
174  
175 extern "C" void RtEnqueueDestruction(void* destructor, void* arg)
176 {
177     std::lock_guard<std::mutex> lock(destructionListMutex);
178     cmajor::rt::destructionList = new cmajor::rt::Destruction(reinterpret_cast<cmajor::rt::destructor_ptr>(destructor), arg, cmajor::rt::destructionList);
179 }
180