Files
Li, Xun 60d36e0de7 Linux 2.10 Open Source Gold Release
Provided a reproducible SDK.
Supported new OS: RHEL 8.1, CentOS 8.1 and Fedora 31.
Supported user to specify platform id in PCK Cert ID Retrieval Tool’s command line option.
Added ability to execute Platform Cert ID Retrieval Tool on multi-package platforms without
  loading enclaves. PCCS now supports this functionality. The platform still needs to support
  SGX.
Updated Platform Cert ID Retrieval Tool and Multi-package registration tool to align with BIOS
  platform manifest changes.
Added .deb and .rpm installers for Platform Cert ID Retrieval Tool and Multi-package
  Registration Agent.
Fixed bugs.

Signed-off-by: Li, Xun <xun.li@intel.com>
2020-07-07 08:42:08 +08:00

919 lines
34 KiB
C++

/*
* Copyright (C) 2011-2020 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 <string>
#include <vector>
#include <iterator>
#include <typeinfo>
#include <functional>
#include <algorithm>
#include <unordered_set>
#include <unordered_map>
#include <initializer_list>
#include <tuple>
#include <memory>
#include <atomic>
#include <mutex>
#include <condition_variable>
#include <map>
#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<char> 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() {};
virtual ~Base() {};
};
/* 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<void()> funct = sample_std_function1;
funct();
//Example with lambda
std::function<void()> 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<int> 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] None 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<typename T>
T sum(T elem)
{
return elem;
}
template<typename T, typename... Args>
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 <typename T> void f(typename T::A*) { printf("[sfinae] First candidate for substitution is matched.\n"); };
/*second candidate for substitution*/
template <typename T> void f(T) { printf("[sfinae] Second candidate for substitution is matched.\n"); }
void ecall_SFINAE_demo()
{
f<int>(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<T> 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<int> &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<int> 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<class F, class A>
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<int>(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<int>(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<int>(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<int> set_of_numbers = { 0, 1, 2, 3, 4, 5 };
const int searchVal = 3;
std::unordered_set<int>::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} 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<int> 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<std::string, int> 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<std::string, int> 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<char> vector_of_letters = { 'A','B','C','D' };
std::map<char, char> map_of_letters = { {'B','b' } };
// Creating a tuple using a tuple constructor
std::tuple<int, std::string> 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("<First element of TupleSample>", 1, 7.9, vector_of_letters, array_of_letters, map_of_letters);
// Access the elements in tupleSample using std::get<index>
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<char> 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<char, char> 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<DemoSmartPtr>("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<DemoSmartPtr> 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<char>(&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<short>(&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<unsigned int>(&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<long>(&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<long long>(&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<std::mutex> 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<std::mutex> 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();
}