/* * Copyright (C) 2011-2017 Intel Corporation. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * Neither the name of Intel Corporation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../Enclave.h" #include "Enclave_t.h" // Feature name : Lambda functions // Feature description : It is used to create a function object that can capture variables in scope. // Demo description : Shows lambda capture options and a some basic usages. void ecall_lambdas_demo() { // Lambdas capture options: int local_var = 0; [] { return true; }; // captures nothing [&] { return ++local_var; }; // captures all variable by reference [&local_var] { return ++local_var; }; // captures local_var by reference [&, local_var] { return local_var; }; // captures all by reference except local_var [=] { return local_var; }; // captures all variable by value [local_var] { return local_var; }; // captures local_var by value [=, &local_var] { return ++local_var; }; // captures all variable by value except local_var // Sample usages for lamdbas: std::vector< int> v { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; printf("[Lambdas] Initial array using lambdas: { "); // Print the elements in an array using lambdas std::for_each(std::begin(v), std::end(v), [](int elem) { printf("%d ", elem); }); //capture specification printf("}.\n"); // Find the first odd number using lambda as an unary predicate when calling find_if. auto first_odd_element = std::find_if(std::begin(v), std::end(v), [=](int elem) { return elem % 2 == 1; }); if (first_odd_element != std::end(v)) printf("[Lambdas] First odd element in the array is %d. \n", *first_odd_element); else printf("[Lambdas] No odd element found in the array.\n"); // Count the even numbers using a lambda function as an unary predicate when calling count_if. long long number_of_even_elements = std::count_if(std::begin(v), std::end(v), [=](int val) { return val % 2 == 0; }); printf("[Lambdas] Number of even elements in the array is %lld.\n", number_of_even_elements); // Sort the elements of an array using lambdas std::sort(std::begin(v), std::end(v), [](int e1, int e2) {return e2 < e1; }); // Print the elements in an array using lambdas printf("[Lambdas] Array after sort: { "); std::for_each(std::begin(v), std::end(v), [](int elem) { printf("%d ", elem); }); printf("}. \n"); printf("\n"); // end of demo } // Feature name : auto // Feature description : It is used for type deduction // Demo description : Shows basic usages of auto specifier with different types. // Helper function for ecall_auto_demo: void sample_func_auto_demo() { printf("[auto] Function sample_func_auto_demo is called. \n"); } void ecall_auto_demo() { double local_var = 0.0; auto a = 7; // Type of variable a is deduced to be int printf("[auto] Type of a is int. typeid = %s.\n", typeid(a).name()); const auto b1 = local_var, *b2 = &local_var; // auto can be used with modifiers like const or &. printf("[auto] Type of b1 is const double. typeid = %s.\n", typeid(b1).name()); printf("[auto] Type of b2 is const double*. typeid = %s.\n", typeid(b2).name()); (void)b1; (void)b2; auto c = 0, *d = &a; // multiple variable initialization if the deduced type does match printf("[auto] Type of c is int. typeid = %s.\n", typeid(c).name()); printf("[auto] Type of d is int*. typeid = %s.\n", typeid(d).name()); (void)c; (void)d; auto lambda = [] {}; // can be used to define lambdas printf("[auto] Type of lambda is [] {}. typeid = %s.\n", typeid(lambda).name()); (void)lambda; auto func = sample_func_auto_demo; // can be used to deduce type of function printf("[auto] Type of func is void(__cdecl*)(void). typeid = %s.\n", typeid(func).name()); func(); printf("\n"); // end of demo } // Feature name : decltype // Feature description : It is used for type deduction // Demo description : Shows basic usages of decltype specifier with different types. void ecall_decltype_demo() { int a = 0 ; decltype(a) b = 0; // create an element of the same type as another element printf("[decltype] Type of b is int. typeid = %s.\n", typeid(b).name()); double c = 0; decltype(a + c) sum = a + c; // deduce type of a sum of elements of different types and create an element of that type. // most usefull in templates. printf("[decltype] Type of sum is double. typeid = %s.\n", typeid(sum).name()); (void)sum; (void)b; printf("\n"); // end of demo } // Feature name : enum classes // Feature description : A new type of enum that solves problems found in old enum like : // unscoping of enum values and the possibility to compare them with int // Demo description : Shows basic usages of enum classes. void ecall_strongly_typed_enum_demo() { // In enum class the underlying type can be set. In the case bellow it is char. enum class DaysOfWeek : char { MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY }; // initialization of variable of type DaysOfWeek DaysOfWeek random_day = DaysOfWeek::MONDAY; (void)random_day; // In is not mandatory to specify the underlying type. enum class Weekend { SATURDAY, SUNDAY }; // The two enum classes above: days_of_week and weekend ilustrate that it is now possible to have two enum classes with the same values in them. // end of demo } // Feature name : Range based for loops // Feature description : Easy to read way of accessing elements in an container. // Demo description : Shows basic usage of range based for loop with c array and vector. void ecall_range_based_for_loops_demo() { char array_of_letters[] = { 'a','b','c','d' }; std::vector vector_of_letters = { 'a','b','c','d' }; printf("[range_based_for_loops] Using range based for loops to print the content of an array: { "); for (auto elem : array_of_letters) printf("%c ", elem); printf("}. \n"); printf("[range_based_for_loops] Using range based for loops to print the content of an vector: { "); for (auto elem : vector_of_letters) printf("%c ", elem); printf("}.\n"); printf("\n"); // end of demo } // Feature name : static_assert // Feature description : It is used to make assertions at compile time. // Demo description : Shows basic usage of static_assert with compile time operation. void ecall_static_assert_demo() { static_assert(sizeof(int) < sizeof(double), "Error : sizeof(int) < sizeof(double) "); const int a = 0; static_assert(a == 0, "Error: value of a is not 0"); // end of demo } // Feature name : New virtual function controls : override, final, default, and delete // Feature description : - delete : a deleted function cannot be inherited // - final : a final function cannot be overrided in the derived class // - default : intruction to the compiler to generate a default function // - override : ensures that a virtual function from derived class overrides a function from base // Demo description : Shows basic usage of new virtual function control. /* Helper class for ecall_virtual_function_control_demo.*/ class Base { public: virtual void f_cannot_be_inherited() final {}; Base(const Base &) = delete; Base() = default; virtual void f_must_be_overrided() {}; }; /* Helper class for ecall_virtual_function_control_demo.*/ class Derived : Base { public: /* The code bellow in this comment does not compile. The function cannot be override because it is declared with keyword final in base virtual double f_cannot_be_inherited() {}; */ /*The keyword override assures that the function overrides a base class member*/ virtual void f_must_be_overrided() override {}; }; void ecall_virtual_function_control_demo() { // The default constructor will be called generated by the compiler with explicit keyword default Base a; // Trying to use the copy contructor will generate code that does not compile because it is deleted // Base b = a; // end of demo } // Feature name : Delegating constructors // Feature description : A class constructors may have common code which can be delegated to a constructor to avoid code repetion // Demo description : Shows basic usage of delegating constructors // Helper class for ecall_delegating_constructors class DemoDelegatingConstructors { int a, b, c; public: DemoDelegatingConstructors(int param_a, int param_b, int param_c) { this->a = param_a; this->b = param_b; this->c = param_c; /*common initialization*/ switch (c) { case 1: printf("[delegating constructors] Called from DemoDelegatingConstructors(int a, int b). \n"); break; case 2: printf("[delegating constructors] Called from DemoDelegatingConstructors(int a). \n"); break; default: printf("[delegating constructors] Called from DemoDelegatingConstructors(int a, int b, int c).\n"); break; } } DemoDelegatingConstructors(int param_a, int param_b) : DemoDelegatingConstructors(param_a, param_b, 1) {} DemoDelegatingConstructors(int param_a) : DemoDelegatingConstructors(param_a, 0, 2) {} }; void ecall_delegating_constructors_demo() { DemoDelegatingConstructors a(1, 2, 3); DemoDelegatingConstructors b(1, 2); DemoDelegatingConstructors c(1); printf("\n"); // end of demo } // Feature name : std::function // Feature description : It is used to store and invoke a callable // Demo description : Shows basic usage of std::function // Helper class for ecall_std_function_demo: void sample_std_function1() { printf("[std_function] calling sample_std_function1\n"); } void ecall_std_function_demo() { // Example with functions std::function funct = sample_std_function1; funct(); //Example with lambda std::function funct_lambda = [] { printf("[std_function] calling a lambda function\n"); }; funct_lambda(); printf("\n"); // end of demo } // Feature name : std::all_of, std::any_of, std::none_of // Feature description : New C++11 algorithms // Demo description : Shows basic usage of the std::all_of, std::any_of, std::none_of. void ecall_cxx11_algorithms_demo() { std::vector v = { 0, 1, 2, 3, 4, 5 }; bool are_all_of = all_of(begin(v), end(v), [](int e) { return e % 2 == 0; }); printf("[cxx11_algorithms] All elements in { 0 1 2 3 4 5 } are even is %s. \n", are_all_of ? "true" : "false"); bool are_any_of = any_of(begin(v), end(v), [](int e) { return e % 2 == 0; }); printf("[cxx11_algorithms] Some elements in { 0 1 2 3 4 5 } are even is %s. \n", are_any_of ? "true" : "false"); bool are_none_of = none_of(begin(v), end(v), [](int e) { return e % 2 == 0; }); printf("[cxx11_algorithms] Some elements in { 0 1 2 3 4 5 } are even is %s. \n", are_none_of ? "true" : "false"); printf("\n"); // end of demo } // Feature name : variadic templates // Feature description : Templates that can have multiple arguments // Demo description : Shows basic usage of variadic templates // Helper template for ecall_variadic_templates_demo: template T sum(T elem) { return elem; } template T sum(T elem1, T elem2, Args... args) { return elem1 + elem2 + sum(args...); } void ecall_variadic_templates_demo() { int computed_sum = sum(1, 2, 3, 4, 5); printf("[variadic_templates] The sum of paramters (1, 2, 3, 4, 5) is %d. \n", computed_sum); printf("\n"); // end of demo } // Feature name : Substitution failure is not an error (SFINAE) // Feature description : Describes the case where a substitution error in templates does not cause errors // Demo description : Shows basic usage of SFINAE /*first candidate for substitution*/ template void f(typename T::A*) { printf("[sfinae] First candidate for substitution is matched.\n"); }; /*second candidate for substitution*/ template void f(T) { printf("[sfinae] Second candidate for substitution is matched.\n"); } void ecall_SFINAE_demo() { f(0x0); // even if the first canditate substition will fail, the second one will pass printf("\n"); // end of demo } //Feature name : Initializer lists //Feature description : An object of type std::initializer_list is a lightweight proxy object that provides access to an array of objects of type const T. //Demo description : Demonstrates the usage of initializer list in the constructor of an object in enclave. class Number { public: Number(const std::initializer_list &v) { for (auto i : v) { elements.push_back(i); } } void print_elements() { printf("[initializer_list] The elements of the vector are:"); for (auto item : elements) { printf(" %d", item); } printf(".\n"); } private: std::vector elements; }; void ecall_initializer_list_demo() { printf("[initializer_list] Using initializer list in the constructor. \n"); Number m = {10, 9, 8, 7, 6, 5, 4, 3, 2, 1}; m.print_elements(); printf("\n"); //end of demo } // Feature name : Rvalue references and move semantics; // Feature description : They are used for memory usage optimazation by eliminating copy operations // Demo description : Shows basic usage of rvalue, move constructor, and move operator // Helper class for ecall_rvalue_demo class DemoBuffer { public: unsigned int size = 100; char *buffer; DemoBuffer(int param_size) { this->size = param_size; buffer = new char[size]; printf("[rvalue] Called constructor : DemoBuffer(int size).\n"); } // A typical copy constructor needs to alocate memory for a new copy // Copying an big array is an expensive operation DemoBuffer(const DemoBuffer & rhs) { this->size = rhs.size; buffer = new char[rhs.size]; memcpy(buffer, rhs.buffer, size); printf("[rvalue] Called copy constructor : DemoBuffer(const DemoBuffer & rhs).\n"); } // A typical move constructor can reuse the memory pointed by the buffer DemoBuffer(DemoBuffer && rhs) { buffer = rhs.buffer; size = rhs.size; // reset state of rhs rhs.buffer = NULL; rhs.size = 0; printf("[rvalue] Called move constructor : DemoBuffer(DemoBuffer && rhs).\n"); } ~DemoBuffer() { delete buffer; } }; // Helper class for ecall_rvalue_demo DemoBuffer foobar(int a) { DemoBuffer x(100); DemoBuffer y(100); if (a > 0) return x; else return y; } void ecall_rvalue_demo() { // This will call the constructor printf("[rvalue] DemoBuffer a(100).\n"); DemoBuffer a(100); printf("[rvalue] DemoBuffer foobar(100). \n"); // Initializing variable d using a temporary object will result in a call to move constructor // This is usefull because it reduces the memory cost of the operation. DemoBuffer d(foobar(100)); // This will call the copy constructor. State of a will not change. printf("[rvalue] DemoBuffer b(a).\n"); DemoBuffer b(a); printf("[rvalue] DemoBuffer c(std::move(a)).\n"); // explicitly cast a to an rvalue so that c will be created using move constructor. // State of a is going to be reseted. DemoBuffer c(std::move(a)); printf("\n"); // end of demo } // Feature name : Nullptr // Feature description : Resolves the issues of converting NULL to integral types // Demo description : Shows basic usage of nullptr // overload candidate 1 void nullptr_overload_candidate(int i) { (void)i; printf("[nullptr] called void nullptr_overload_candidate(int i).\n"); } // overload candidate 2 void nullptr_overload_candidate(int* ptr) { (void)ptr; printf("[nullptr] called void nullptr_overload_candidate(int* ptr).\n"); } template void Fwd(F f, A a) { f(a); } void g(int* i) { (void)i; printf("[nullptr] Function %s called\n", __FUNCTION__); } // Feature name : // Feature description : // Demo description : void ecall_nullptr_demo() { // NULL can be converted to integral types() like int and will call overload candidate 1 nullptr_overload_candidate(NULL); // nullptr can't be converted to integral types() like int and will call overload candidate 2 nullptr_overload_candidate(nullptr); g(NULL); // Fine g(0); // Fine Fwd(g, nullptr); // Fine //Fwd(g, NULL); // ERROR: No function g(int) printf("\n"); // end of demo } // Feature name : Scoped enums // Feature description : // Demo description : enum class Color { orange, brown, green = 30, blue, red }; void ecall_enum_class_demo() { int n = 0; Color color1 = Color::brown; switch (color1) { case Color::orange: printf("[enum class] orange"); break; case Color::brown: printf("[enum class] brown"); break; case Color::green: printf("[enum class] green"); break; case Color::blue: printf("[enum class] blue"); break; case Color::red: printf("[enum class] red"); break; } // n = color1; // Not allowed: no scoped enum to int conversion n = static_cast(color1); // OK, n = 1 printf(" - int = %d\n", n); Color color2 = Color::red; switch (color2) { case Color::orange: printf("[enum class] orange"); break; case Color::brown: printf("[enum class] brown"); break; case Color::green: printf("[enum class] green"); break; case Color::blue: printf("[enum class] blue"); break; case Color::red: printf("[enum class] red"); break; } n = static_cast(color2); // OK, n = 32 printf(" - int = %d\n", n); Color color3 = Color::green; switch (color3) { case Color::orange: printf("[enum class] orange"); break; case Color::brown: printf("[enum class] brown"); break; case Color::green: printf("[enum class] green"); break; case Color::blue: printf("[enum class] blue"); break; case Color::red: printf("[enum class] red"); break; } n = static_cast(color3); // OK, n = 30 printf(" - int = %d\n", n); printf("\n"); } // Feature name : new container classes // Feature description : unordered_set, unordered_map, unordered_multiset, and unordered_multimap // Demo description : Shows basic usage of new container classes void ecall_new_container_classes_demo() { // unordered_set // container used for fast acces that groups elements in buckets based on their hash std::unordered_set set_of_numbers = { 0, 1, 2, 3, 4, 5 }; const int searchVal = 3; std::unordered_set::const_iterator got = set_of_numbers.find(searchVal); if (got == set_of_numbers.end()) printf("[new_container_classes] unordered_set { 0, 1, 2, 3, 4, 5} has value 3.\n"); else printf("[new_container_classes] unordered_set { 0, 1, 2, 3, 4, 5} it does not have value 3.\n"); // unordered_multiset // container used for fast acces that groups non unique elements in buckets based on their hash std::unordered_multiset multiset_of_numbers = { 0, 1, 2, 3, 3, 3 }; printf("[new_container_classes] multiset_set { 0, 1, 2, 3, 3, 3} has %d elements with value %d.\n", (int)multiset_of_numbers.count(searchVal), searchVal); // unordered_map std::unordered_map grades{ { "A", 10 },{ "B", 8 },{ "C", 7 },{ "D", 5 },{ "E", 3 } }; printf("[new_container_classes] unordered_map elements: {"); for (auto pair : grades) { printf("[%s %d] ", pair.first.c_str(), pair.second); } printf("}.\n"); // unordered_multimap std::unordered_multimap multimap_grades{ { "A", 10 },{ "B", 8 },{ "B", 7 },{ "E", 5 },{ "E", 3 },{ "E",1 } }; printf("[new_container_classes] unordered_multimap elements: {"); for (auto pair : multimap_grades) { printf("[%s %d] ", pair.first.c_str(), pair.second); } printf("}.\n"); printf("\n"); // end of demo } // Feature name : Tuple // Feature description : Objects that pack elements of multiple types which can be accessed by index // Demo description : Shows basic usage of tuple: creation and access void ecall_tuple_demo() { // Create tuple using std::make_tuple char array_of_letters[4] = {'A','B','C','D'}; std::vector vector_of_letters = { 'A','B','C','D' }; std::map map_of_letters = { {'B','b' } }; // Creating a tuple using a tuple constructor std::tuple tuple_sample_with_constructor(42, "Sample tuple"); (void)tuple_sample_with_constructor; // Creating a tuple using std::make_tuple auto tuple_sample = std::make_tuple("", 1, 7.9, vector_of_letters, array_of_letters, map_of_letters); // Access the elements in tupleSample using std::get printf("[tuple] show first element in TupleSample: %s. \n", std::get<0>(tuple_sample)); printf("[tuple] show second element in TupleSample: %d. \n", std::get<1>(tuple_sample)); printf("[tuple] show third element in TupleSample: %f. \n", std::get<2>(tuple_sample)); // Getting vector from a tuple std::vector temp_vector = std::get<3>(tuple_sample); (void)temp_vector; // Getting array from a tuple int first_elem_of_array = std::get<4>(tuple_sample)[0]; (void)first_elem_of_array; // Getting map from a tuple std::map temp_map = std::get<5>(tuple_sample); (void)temp_map; printf("\n"); // end of demo } // Feature name : new smart pointer // Feature description : shared_ptr and unique_ptr // Demo decription : Shows basic usage of smart pointers // Helper class for ecall_shared_ptr_demo class DemoSmartPtr { std::string smartPointerType; public: DemoSmartPtr(std::string param_smartPointerType) { printf("[smart_ptr] In construct of object demo_smart_ptr using %s. \n", param_smartPointerType.c_str()); this->smartPointerType = param_smartPointerType; } ~DemoSmartPtr() { printf("[smart_ptr] In deconstructor of object demo_smart_ptr using %s. \n", smartPointerType.c_str()); } }; void ecall_shared_ptr_demo() { // std::shared_ptr is smart pointer that takes ownership of an object using a pointer // The object is freed when the last smart_pointer does not point to it. // Creating a shared pointer using std::make_shared auto shared_ptr = std::make_shared("smart_ptr."); // The constructor of DemoSmartPtr will be called here printf("[smart_ptr] shared_ptr reference count = %ld. \n", shared_ptr.use_count()); auto shared_ptr2 = shared_ptr; printf("[smart_ptr] shared_ptr reference count = %ld incresead after creating another shared pointer.\n", shared_ptr.use_count()); shared_ptr2.reset(); printf("[smart_ptr] shared_ptr reference count = %ld decresead after calling releasing ownership. \n", shared_ptr.use_count()); // std::unique_ptr is smart pointer that takes ownership of an object using a pointer // it is different from smart_ptr in the sense that only one unique_ptr can take ownership std::unique_ptr unique_ptr(new DemoSmartPtr("unique_ptr")); // When going out of scope both shared_ptr and unique_ptr release the objects they own // end of demo } //Feature name : atomic //Feature description: The atomic library provides components for fine-grained atomic operations allowing for lockless concurrent programming. // Each atomic operation is indivisible with regards to any other atomic operation that involves the same object. // Atomic objects are free of data races. //Demo description : Demonstrates the usage of atomic types, objects and functions in enclave. void ecall_atomic_demo() { printf("[atomic] Atomic types, objects and functions demo.\n"); printf("[atomic_store] Defining an atomic_char object with an initial value of 5.\n"); std::atomic_char atc(5); printf("[atomic_store] The current value stored in the atomic object is: %d\n", atc.load()); printf("[atomic_store] Replacing the value of the atomic object with a non-atomic value of 3.\n"); std::atomic_store(&atc, 3); printf("[atomic_store] The new value of the atomic object is: %d.\n", atc.load()); printf("\n"); printf("[atomic_store_explicit] Defining an atomic_short object with an initial value of 5.\n"); std::atomic_short ats(5); printf("[atomic_store_explicit] The current value stored in the atomic object is: %d.\n", ats.load()); printf("[atomic_store_explicit] Replacing the value of the atomic object with a non-atomic value of 3.\n"); std::atomic_store_explicit(&ats, 3, std::memory_order_seq_cst); printf("[atomic_store] The new value of the atomic object is: %d.\n", ats.load()); printf("\n"); printf("[atomic_load] Defining an atomic_int object with an initial value of 4.\n"); std::atomic_int ati1(4); printf("[atomic_load] Obtaining the value of the atomic object and saving it in a int variable.\n"); int val = std::atomic_load(&ati1); printf("[atomic_load] The obtained value is %d.\n", val); printf("\n"); printf("[atomic_load_explicit] Defining an atomic_int object with an initial value of 2.\n"); std::atomic_int ati2(2); printf("[atomic_load_explicit] Obtaining the value of the atomic object and saving it in a int variable.\n"); int val1 = std::atomic_load_explicit(&ati2, std::memory_order_seq_cst); printf("[atomic_load_explicit] The obtained value is %d.\n", val1); printf("\n"); printf("[atomic_fetch_add] Defining an atomic_int object with an initial value of 7.\n"); std::atomic_int ati(7); printf("[atomic_fetch_add] The current value stored in the atomic object is: %d.\n", ati.load()); printf("[atomic_fetch_add] Adding a non-atomic value of 8 to the atomic object.\n"); std::atomic_fetch_add(&ati, 8); printf("[atomic_fetch_add] The new value of the atomic object is: %d.\n", ati.load()); printf("\n"); printf("[atomic_fetch_add_explicit] Defining an atomic_uint object with an initial value of 7.\n"); std::atomic_uint atui(7); printf("[atomic_fetch_add_explicit] The current value stored in the atomic object is: %u.\n", atui.load()); printf("[atomic_fetch_add_explicit] Adding a non-atomic value of 8 to the atomic object.\n"); std::atomic_fetch_add_explicit(&atui, 8, std::memory_order_seq_cst); printf("[atomic_fetch_add_explicit] The new value of the atomic object is: %u.\n", atui.load()); printf("\n"); printf("[atomic_fetch_sub] Defining an atomic_long object with an initial value of 20.\n"); std::atomic_long atl(20); printf("[atomic_fetch_sub] The current value stored in the atomic object is: %ld.\n", atl.load()); printf("[atomic_fetch_sub] Substracting a non-atomic value of 8 from the value of the atomic object.\n"); std::atomic_fetch_sub(&atl, 8); printf("[atomic_fetch_sub] The new value of the atomic object is: %ld.\n", atl.load()); printf("\n"); printf("[atomic_fetch_sub_explicit] Defining an atomic_llong object with an initial value of 20.\n"); std::atomic_llong atll(20); printf("[atomic_fetch_sub_explicit] The current value stored in the atomic object is: %lld.\n", atll.load()); printf("[atomic_fetch_sub_explicit] Substracting a non-atomic value of 8 from the value of the atomic object.\n"); std::atomic_fetch_sub_explicit(&atll, 8, std::memory_order_seq_cst); printf("[atomic_fetch_sub_explicit] The new value of the atomic object is: %lld.\n", atll.load()); printf("\n"); // end of demo } //Feature name : mutex //Feature description : The mutex class is a synchronization primitive that can be used to protect shared data // from being simultaneously accessed by multiple threads. //Demo description : Demonstrates mutex protection when incrementing values in multiple threads. //Structure used in mutex demo to show the behavior without using a mutex struct CounterWithoutMutex { int value; CounterWithoutMutex() : value(0) {} void increment() { ++value; } }; CounterWithoutMutex counter_without_protection; //E-call used by mutex demo to perform the incrementation using a counter without mutex protection void ecall_mutex_demo_no_protection() { for (int i = 0; i < 100000; ++i) { counter_without_protection.increment(); } } //E-call used by mutex demo to get the final value of the counter from enclave void ecall_print_final_value_no_protection() { printf("[mutex] Incrementing values in three threads without mutex protection, using a 100000 times loop. \n[mutex]Expected value is 300000. The final value is %d.\n", counter_without_protection.value); } //Structure used in mutex demo struct CounterProtectedByMutex { std::mutex mutex; int value; CounterProtectedByMutex() : value(0) {} void increment() { //locking the mutex to avoid simultaneous incrementation in different threads mutex.lock(); ++value; //unlocking the mutex mutex.unlock(); } }; CounterProtectedByMutex counter_with_protection; //E-call used by mutex demo to perform the actual incrementation void ecall_mutex_demo() { for (int i = 0; i < 100000; ++i) { counter_with_protection.increment(); } } //E-call used by mutex demo to get the final value of the counter from enclave void ecall_print_final_value_mutex_demo() { printf("[mutex] Mutex protection when incrementing a value in 3 threads, using a 100000 times loop. \n[mutex]Expected value is 300000. The final value is %d.\n", counter_with_protection.value); } //Feature name : condition_variable //Feature description: The condition_variable class is a synchronization primitive that can be used to block a thread, // or multiple threads at the same time, until another thread both modifies a shared variable (the condition), // and notifies the condition_variable. //Demo description : Demonstrates condition_variable usage in a two threads environment. One thread is used for loading the data and // the other processes the loaded data. The thread for processing the data waits untill the data is loaded in the // other thread and gets notified when loading is completed. //This class is used by condition variable demo class DemoConditionVariable { std::mutex mtx; std::condition_variable cond_var; bool data_loaded; public: DemoConditionVariable() { data_loaded = false; } void load_data() { //Simulating loading of the data printf("[condition_variable] Loading Data...\n"); { // Locking the data structure std::lock_guard guard(mtx); // Setting the flag to true to signal load data completion data_loaded = true; } // Notify to unblock the waiting thread cond_var.notify_one(); } bool is_data_loaded() { return data_loaded; } void main_task() { printf("\n"); printf("[condition_variable] Running condition variable demo.\n"); // Acquire the lock std::unique_lock lck(mtx); printf("[condition_variable] Waiting for the data to be loaded in the other thread.\n"); cond_var.wait(lck, std::bind(&DemoConditionVariable::is_data_loaded, this)); printf("[condition_variable] Processing the loaded data.\n"); printf("[condition_variable] Done.\n"); } }; DemoConditionVariable app; //E-call used by condition_variable demo - processing thread void ecall_condition_variable_run() { app.main_task(); } //E-call used by condifion_variable demo - loader thread void ecall_condition_variable_load() { app.load_data(); }