EMANE  1.2.1
bitpool.inl
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2013-2014,2016 - Adjacent Link LLC, Bridgewater, New
3  * Jersey
4  * Copyright (c) 2009-2010 - DRS CenGen, LLC, Columbia, Maryland
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * * Redistributions of source code must retain the above copyright
12  * notice, this list of conditions and the following disclaimer.
13  * * Redistributions in binary form must reproduce the above copyright
14  * notice, this list of conditions and the following disclaimer in
15  * the documentation and/or other materials provided with the
16  * distribution.
17  * * Neither the name of DRS CenGen, LLC nor the names of its
18  * contributors may be used to endorse or promote products derived
19  * from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  * POSSIBILITY OF SUCH DAMAGE.
33  */
34 
35 #include "bitpool.h"
36 
37 #include <cmath>
38 #include <condition_variable>
39 
40 namespace
41 {
42  const EMANE::Microseconds FillIntervalMicroseconds{1000000};
43 }
44 
45 
46 inline
48  pPlatformService_{pPlatformService},
49  id_(id),
50  u64MaxSize_{},
51  u64CurrentSize_{},
52  lastRequestTime_{},
53  fFillRemainder_{}
54 { }
55 
56 
57 inline
59 { }
60 
61 
62 
63 inline
64  void
65 EMANE::Utils::BitPool::setMaxSize(std::uint64_t u64NewSize)
66 {
67  // lock mutex
68  std::lock_guard<std::mutex> m(mutex_);
69 
70  // size changed
71  if(u64NewSize != u64MaxSize_)
72  {
73  LOGGER_VERBOSE_LOGGING(pPlatformService_->logService(),
75  "NEM %03hu BitPool::%s current pool size %ju, new pool size %ju",
76  id_,
77  __func__,
78  u64MaxSize_,
79  u64NewSize);
80 
81  // new size is less than current size
82  if(u64CurrentSize_ > u64NewSize)
83  {
84  // reduce current size
85  u64CurrentSize_ = u64NewSize;
86  }
87 
88  // set new max size
89  u64MaxSize_ = u64NewSize;
90  }
91 }
92 
93 
94 
95 inline
96 std::uint64_t
97 EMANE::Utils::BitPool::get(std::uint64_t u64Request, bool bFullFill)
98 {
99  // lock mutex
100  std::lock_guard<std::mutex> m(mutex_);
101 
102  // disabled
103  if(u64MaxSize_ == 0 || u64Request == 0)
104  {
105  // nothing outstanding
106  return 0;
107  }
108 
109  // total acquired
110  std::uint64_t u64Acquired{};
111 
112  // try to fulfill request
113  while(1)
114  {
115  TimePoint currentTime = Clock::now();
116 
117  // time to wait interval
118  Microseconds waitIntervalMicroseconds;
119 
120  // make request
121  u64Acquired += doDrainPool(u64Request - u64Acquired, currentTime, waitIntervalMicroseconds);
122 
123  LOGGER_VERBOSE_LOGGING(pPlatformService_->logService(),
124  DEBUG_LEVEL,
125  "NEM %03hu BitPool::%s request %ju, acquired %ju, pool remaining %ju, %4.2f%%",
126  id_,
127  __func__,
128  u64Request,
129  u64Acquired,
130  u64CurrentSize_,
131  100.0f * (1.0f - (static_cast<float>(u64MaxSize_ - u64CurrentSize_) /
132  static_cast<float>(u64MaxSize_))));
133 
134  // request fulfilled
135  if(!bFullFill || u64Acquired == u64Request)
136  {
137  // done
138  break;
139  }
140 
141  // local mutex
142  std::mutex mutex;
143 
144  // condition variable
145  std::condition_variable cond{};
146 
147  std::unique_lock<std::mutex> lock(mutex);
148 
149  // wait here
150  if(cond.wait_until(lock,currentTime + waitIntervalMicroseconds) != std::cv_status::timeout) {
151 
152  // did not time out, spurious wake up
153  break;
154  }
155  }
156 
157  // return the result
158  return u64Request - u64Acquired;
159 }
160 
161 
162 inline
163 std::uint64_t
165 {
166  // lock mutex
167  std::lock_guard<std::mutex> m(mutex_);
168 
169  return u64CurrentSize_;
170 }
171 
172 
173 inline
174 std::uint64_t
175 EMANE::Utils::BitPool::doDrainPool(std::uint64_t u64Request,
176  const TimePoint & requestTime,
177  Microseconds & intervalMicroseconds)
178 {
179  std::uint64_t u64Acquired{};
180 
181  // bring the pool up to date since last request
182  doFillPool(requestTime);
183 
184  // drain the pool
185  if(u64Request > u64CurrentSize_)
186  {
187  // size outstanding
188  std::uint64_t u64Outstanding{u64Request - u64CurrentSize_};
189 
190  // result is the available pool
191  u64Acquired = u64CurrentSize_;
192 
193  // empty the pool
194  u64CurrentSize_ = 0;
195 
196  // fill ratio assumes 1 sec interval
197  float fSeconds{static_cast<float>(u64Outstanding) / static_cast<float>(u64MaxSize_)};
198 
199  // set wait interval
200  intervalMicroseconds = std::chrono::duration_cast<Microseconds>(DoubleSeconds{fSeconds});
201 
202  // cap wait interval to fill interval
203  if(intervalMicroseconds > FillIntervalMicroseconds)
204  {
205  intervalMicroseconds = FillIntervalMicroseconds;
206  }
207  }
208  else
209  {
210  // result is the request
211  u64Acquired = u64Request;
212 
213  // drain the pool by the request size
214  u64CurrentSize_ -= u64Request;
215 
216  // no wait time
217  intervalMicroseconds = Microseconds::zero();
218  }
219 
220  // update last request time
221  lastRequestTime_ = requestTime;
222 
223  // return result
224  return u64Acquired;
225 }
226 
227 
228 inline
229 void
230 EMANE::Utils::BitPool::doFillPool(const TimePoint & requestTime)
231 {
232  // fill the pool since last request
233  if(lastRequestTime_ != TimePoint{})
234  {
235  // time since last request
236  Microseconds deltaTMicroseconds{std::chrono::duration_cast<Microseconds>(requestTime - lastRequestTime_)};
237 
238  // fill the pool depending on elapsed time
239  if(deltaTMicroseconds >= FillIntervalMicroseconds)
240  {
241  // fill to full capacity
242  u64CurrentSize_ = u64MaxSize_;
243 
244  fFillRemainder_ = 0;
245  }
246  else
247  {
248  // find the amount of time into the fill interval
249  Microseconds diffMicroseconds{FillIntervalMicroseconds - (FillIntervalMicroseconds - deltaTMicroseconds)};
250 
251  // accumulate fill amount for this interval
252  fFillRemainder_ += (static_cast<float>(diffMicroseconds.count()) /
253  static_cast<float>(FillIntervalMicroseconds.count())) * u64MaxSize_;
254 
255  // have at least a whole unit
256  if(fFillRemainder_ >= 1.0f)
257  {
258  std::uint64_t u64Fill{static_cast<std::uint64_t>(fFillRemainder_)};
259 
260  // fill pool
261  if((u64CurrentSize_ + u64Fill) <= u64MaxSize_)
262  {
263  u64CurrentSize_ = u64CurrentSize_ + u64Fill;
264  }
265  else
266  {
267  u64CurrentSize_ = u64MaxSize_;
268  }
269 
270  // keep fraction remainder
271  fFillRemainder_ -= u64Fill;
272  }
273  }
274  }
275 }
BitPool(PlatformServiceProvider *pPlatformService, NEMId id)
Definition: bitpool.inl:47
#define LOGGER_VERBOSE_LOGGING(logger, level, fmt, args...)
void setMaxSize(std::uint64_t u64NewSize)
Definition: bitpool.inl:65
The PlatformServiceProvider interface provides access to emulator services.
std::uint64_t get(std::uint64_t u64Request, bool bFullFill=true)
Definition: bitpool.inl:97
std::chrono::microseconds Microseconds
Definition: types.h:45
std::chrono::duration< double > DoubleSeconds
Definition: types.h:47
std::uint16_t NEMId
Definition: types.h:52
std::uint64_t getCurrentSize()
Definition: bitpool.inl:164
virtual LogServiceProvider & logService()=0
Clock::time_point TimePoint
Definition: types.h:50