1 /***
2  * Copyright (C) Microsoft. All rights reserved.
3  * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
4  *
5  * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
6  *
7  * Parallel Patterns Library implementation (common code across platforms)
8  *
9  * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
10  *
11  * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
12  ****/
13 
14 #include "stdafx.h"
15 
16 #if !defined(_WIN32) || CPPREST_FORCE_PPLX
17 #include "pplx/pplx.h"
18 #include <atomic>
19 
20 namespace pplx
21 {
22 namespace details
23 {
24 /// <summary>
25 /// Spin lock to allow for locks to be used in global scope
26 /// </summary>
27 class _Spin_lock
28 {
29 public:
_Spin_lock()30     _Spin_lock() : _M_lock() {}
31 
lock()32     void lock()
33     {
34         while (_M_lock.test_and_set())
35         {
36             pplx::details::platform::YieldExecution();
37         }
38     }
39 
unlock()40     void unlock() { _M_lock.clear(); }
41 
42 private:
43     std::atomic_flag _M_lock;
44 };
45 
46 typedef ::pplx::scoped_lock<_Spin_lock> _Scoped_spin_lock;
47 } // namespace details
48 
49 static struct _pplx_g_sched_t
50 {
51     typedef std::shared_ptr<pplx::scheduler_interface> sched_ptr;
52 
_pplx_g_sched_tpplx::_pplx_g_sched_t53     _pplx_g_sched_t() { m_state.store(post_ctor, std::memory_order_relaxed); }
54 
~_pplx_g_sched_tpplx::_pplx_g_sched_t55     ~_pplx_g_sched_t() { m_state.store(post_dtor, std::memory_order_relaxed); }
56 
get_schedulerpplx::_pplx_g_sched_t57     sched_ptr get_scheduler()
58     {
59         sched_ptr result;
60         switch (m_state.load(std::memory_order_relaxed))
61         {
62             case post_ctor:
63                 // This is the 99.9% case.
64                 {
65                     ::pplx::details::_Scoped_spin_lock lock(m_spinlock);
66                     if (!m_scheduler)
67                     {
68                         m_scheduler = std::make_shared<::pplx::default_scheduler_t>();
69                     }
70 
71                     result = m_scheduler;
72                 } // unlock
73 
74                 break;
75             default:
76                 // This case means the global m_scheduler is not available.
77                 // We spin off an individual scheduler instead.
78                 result = std::make_shared<::pplx::default_scheduler_t>();
79                 break;
80         }
81 
82         return result;
83     }
84 
set_schedulerpplx::_pplx_g_sched_t85     void set_scheduler(sched_ptr scheduler)
86     {
87         const auto localState = m_state.load(std::memory_order_relaxed);
88         if (localState == pre_ctor || localState == post_dtor)
89         {
90             throw invalid_operation("Scheduler cannot be initialized now");
91         }
92 
93         ::pplx::details::_Scoped_spin_lock lock(m_spinlock);
94 
95         if (m_scheduler)
96         {
97             throw invalid_operation("Scheduler is already initialized");
98         }
99 
100         m_scheduler = std::move(scheduler);
101     }
102 
103     enum m_state_values
104     {
105         pre_ctor,
106         post_ctor,
107         post_dtor
108     };
109 
110 private:
111     std::atomic<m_state_values> m_state;
112     pplx::details::_Spin_lock m_spinlock;
113     sched_ptr m_scheduler;
114 } _pplx_g_sched;
115 
get_ambient_scheduler()116 _PPLXIMP std::shared_ptr<pplx::scheduler_interface> _pplx_cdecl get_ambient_scheduler()
117 {
118     return _pplx_g_sched.get_scheduler();
119 }
120 
set_ambient_scheduler(std::shared_ptr<pplx::scheduler_interface> _Scheduler)121 _PPLXIMP void _pplx_cdecl set_ambient_scheduler(std::shared_ptr<pplx::scheduler_interface> _Scheduler)
122 {
123     _pplx_g_sched.set_scheduler(std::move(_Scheduler));
124 }
125 
126 } // namespace pplx
127 
128 #endif
129