1 // ethash: C/C++ implementation of Ethash, the Ethereum Proof of Work algorithm.
2 // Copyright 2018 Pawel Bylica.
3 // Licensed under the Apache License, Version 2.0. See the LICENSE file.
4 
5 #include "ethash-internal.hpp"
6 
7 #include <memory>
8 #include <mutex>
9 
10 #if !defined(__has_cpp_attribute)
11 #define __has_cpp_attribute(x) 0
12 #endif
13 
14 #if __has_cpp_attribute(gnu::noinline)
15 #define ATTRIBUTE_NOINLINE [[gnu::noinline]]
16 #elif _MSC_VER
17 #define ATTRIBUTE_NOINLINE __declspec(noinline)
18 #else
19 #define ATTRIBUTE_NOINLINE
20 #endif
21 
22 namespace ethash
23 {
24 namespace
25 {
26 std::mutex shared_context_mutex;
27 std::shared_ptr<epoch_context> shared_context;
28 thread_local std::shared_ptr<epoch_context> thread_local_context;
29 
30 std::mutex shared_context_full_mutex;
31 std::shared_ptr<epoch_context_full> shared_context_full;
32 thread_local std::shared_ptr<epoch_context_full> thread_local_context_full;
33 
34 /// Update thread local epoch context.
35 ///
36 /// This function is on the slow path. It's separated to allow inlining the fast
37 /// path.
38 ///
39 /// @todo: Redesign to guarantee deallocation before new allocation.
40 ATTRIBUTE_NOINLINE
update_local_context(int epoch_number)41 void update_local_context(int epoch_number)
42 {
43     // Release the shared pointer of the obsoleted context.
44     thread_local_context.reset();
45 
46     // Local context invalid, check the shared context.
47     std::lock_guard<std::mutex> lock{shared_context_mutex};
48 
49     if (!shared_context || shared_context->epoch_number != epoch_number)
50     {
51         // Release the shared pointer of the obsoleted context.
52         shared_context.reset();
53 
54         // Build new context.
55         shared_context = create_epoch_context(epoch_number);
56     }
57 
58     thread_local_context = shared_context;
59 }
60 
61 ATTRIBUTE_NOINLINE
update_local_context_full(int epoch_number)62 void update_local_context_full(int epoch_number)
63 {
64     // Release the shared pointer of the obsoleted context.
65     thread_local_context_full.reset();
66 
67     // Local context invalid, check the shared context.
68     std::lock_guard<std::mutex> lock{shared_context_full_mutex};
69 
70     if (!shared_context_full || shared_context_full->epoch_number != epoch_number)
71     {
72         // Release the shared pointer of the obsoleted context.
73         shared_context_full.reset();
74 
75         // Build new context.
76         shared_context_full = create_epoch_context_full(epoch_number);
77     }
78 
79     thread_local_context_full = shared_context_full;
80 }
81 }  // namespace
82 
get_global_epoch_context(int epoch_number)83 const epoch_context& get_global_epoch_context(int epoch_number)
84 {
85     // Check if local context matches epoch number.
86     if (!thread_local_context || thread_local_context->epoch_number != epoch_number)
87         update_local_context(epoch_number);
88 
89     return *thread_local_context;
90 }
91 
get_global_epoch_context_full(int epoch_number)92 const epoch_context_full& get_global_epoch_context_full(int epoch_number)
93 {
94     // Check if local context matches epoch number.
95     if (!thread_local_context_full || thread_local_context_full->epoch_number != epoch_number)
96         update_local_context_full(epoch_number);
97 
98     return *thread_local_context_full;
99 }
100 }  // namespace ethash
101