mirror of https://github.com/deavmi/doap
Merge pull request #8 from deavmi/feature/exchange_lifetime
Implement EXCHANGE_LIFETIME for message ID generation
This commit is contained in:
commit
481afbb194
|
@ -8,6 +8,8 @@ import core.sync.mutex : Mutex;
|
|||
import core.sync.condition : Condition;
|
||||
import std.container.slist : SList;
|
||||
import core.thread : dur, Duration, Thread;
|
||||
import std.datetime.stopwatch : StopWatch, AutoStart;
|
||||
import doap.utils : findNextFree;
|
||||
|
||||
/**
|
||||
* A CoAP client
|
||||
|
@ -40,11 +42,15 @@ public class CoapClient
|
|||
*/
|
||||
private Mutex requestsLock;
|
||||
|
||||
/**
|
||||
* Rolling Message ID
|
||||
/**
|
||||
* Message IDs and lifetime map
|
||||
*/
|
||||
private ushort rollingMid;
|
||||
private Mutex rollingLock;
|
||||
private StopWatch[ushort] mids;
|
||||
|
||||
/**
|
||||
* Lock for the above
|
||||
*/
|
||||
private Mutex midsLock;
|
||||
|
||||
/**
|
||||
* Creates a new CoAP client to the
|
||||
|
@ -61,32 +67,71 @@ public class CoapClient
|
|||
this.messaging = new UDPMessaging(this); //UDP transport
|
||||
|
||||
this.requestsLock = new Mutex();
|
||||
|
||||
this.rollingMid = 0;
|
||||
this.rollingLock = new Mutex();
|
||||
this.midsLock = new Mutex();
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Maximum lifetime of a message ID before
|
||||
* it is considered for re-use
|
||||
*/
|
||||
private Duration EXCHANGE_LIFETIME = dur!("msecs")(180);
|
||||
|
||||
/**
|
||||
* Sets the exchange lifetime. In other words
|
||||
* the duration of time that must pass before
|
||||
* a message ID is considered free-for-use again
|
||||
*
|
||||
* Params:
|
||||
* lifetime = the lifetime duration
|
||||
*/
|
||||
public void setExchangeLifetime(Duration lifetime)
|
||||
{
|
||||
this.EXCHANGE_LIFETIME = lifetime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a new message ID
|
||||
*
|
||||
* Returns: the next message id
|
||||
*/
|
||||
private final ushort newMid()
|
||||
private final ushort newMid2()
|
||||
{
|
||||
ushort newValue;
|
||||
|
||||
// Lock rolling counter
|
||||
this.rollingLock.lock();
|
||||
this.midsLock.lock();
|
||||
|
||||
newValue = this.rollingMid;
|
||||
this.rollingMid++;
|
||||
scope(exit)
|
||||
{
|
||||
// Unlock rolling counter
|
||||
this.midsLock.unlock();
|
||||
}
|
||||
|
||||
// Unlock rolling counter
|
||||
this.rollingLock.unlock();
|
||||
// Message IDs which are in use
|
||||
ushort[] inUse;
|
||||
|
||||
return newValue;
|
||||
foreach(ushort occupied; this.mids.keys())
|
||||
{
|
||||
// Peek the value of the stopwatch
|
||||
if(this.mids[occupied].peek() >= EXCHANGE_LIFETIME)
|
||||
{
|
||||
// It's expired, so we can use it (first reset the time)
|
||||
this.mids[occupied].reset();
|
||||
|
||||
return occupied;
|
||||
}
|
||||
else
|
||||
{
|
||||
inUse ~= occupied;
|
||||
}
|
||||
}
|
||||
|
||||
// If none was available for re-use then find next available
|
||||
// ... free and use that (also don't forget to register it)
|
||||
ushort newMid = findNextFree(inUse);
|
||||
this.mids[newMid] = StopWatch(AutoStart.yes);
|
||||
|
||||
return newMid;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -172,7 +217,7 @@ public class CoapClient
|
|||
requestPacket.setCode(requestBuilder.requestCode);
|
||||
requestPacket.setPayload(requestBuilder.pyld);
|
||||
requestPacket.setToken(requestBuilder.tkn);
|
||||
requestPacket.setMessageId(newMid());
|
||||
requestPacket.setMessageId(newMid2());
|
||||
|
||||
// Create the future
|
||||
CoapRequestFuture future = new CoapRequestFuture();
|
||||
|
@ -319,7 +364,15 @@ version(unittest)
|
|||
/**
|
||||
* Client testing
|
||||
*
|
||||
* Tests the rolling of the message id
|
||||
* Tests the rolling of the message id,
|
||||
* here I configure the `EXCHANGE_LIFETIME`
|
||||
* to be a value high enough to not have
|
||||
* them quickly expire.
|
||||
*
|
||||
* NOTE: In the future it may be calculated
|
||||
* in relation to other variables and we may
|
||||
* need a private method accessible here that
|
||||
* can override it
|
||||
*/
|
||||
unittest
|
||||
{
|
||||
|
@ -332,6 +385,9 @@ unittest
|
|||
.post();
|
||||
|
||||
|
||||
// Set it to something high enough (TODO: Change this later)
|
||||
client.setExchangeLifetime(dur!("msecs")(300));
|
||||
|
||||
writeln("Future start (first)");
|
||||
CoapPacket response = future.get();
|
||||
writeln("Future done (first)");
|
||||
|
|
|
@ -121,19 +121,85 @@ unittest
|
|||
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given value is present in
|
||||
* the given array
|
||||
*
|
||||
* Params:
|
||||
* array = the array to check against
|
||||
* value = the value to check prescence
|
||||
* for
|
||||
* Returns: `true` if present, `false`
|
||||
* otherwise
|
||||
*/
|
||||
public bool isPresent(T)(T[] array, T value)
|
||||
{
|
||||
if(array.length == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach(T cur; array)
|
||||
{
|
||||
if(cur == value)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the `isPresent!(T)(T[], T)` function
|
||||
*/
|
||||
unittest
|
||||
{
|
||||
version(LittleEndian)
|
||||
ubyte[] values = [1,2,3];
|
||||
foreach(ubyte value; values)
|
||||
{
|
||||
ushort i = 1;
|
||||
writeln("Pre-order: ", i);
|
||||
ushort ordered = order(i, Order.BE);
|
||||
writeln("Post-order: ", ordered);
|
||||
assert(ordered == 256);
|
||||
assert(isPresent(values, value));
|
||||
}
|
||||
else version(BigEndian)
|
||||
assert(isPresent(values, 0) == false);
|
||||
assert(isPresent(values, 5) == false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an array of values this tries to find
|
||||
* the next free value of which is NOT present
|
||||
* within the given array
|
||||
*
|
||||
* Params:
|
||||
* used = the array of values
|
||||
* Returns: the free value
|
||||
*/
|
||||
public T findNextFree(T)(T[] used) if(__traits(isIntegral, T))
|
||||
{
|
||||
T found;
|
||||
if(used.length == 0)
|
||||
{
|
||||
// TODO: Add this AND CI tests for it
|
||||
return 0;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
found = 0;
|
||||
while(isPresent(used, found)) // FIXME: Constant loop if none available
|
||||
{
|
||||
found++;
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the `findNextFree!(T)(T[])` function
|
||||
*/
|
||||
unittest
|
||||
{
|
||||
ubyte[] values = [1,2,3];
|
||||
ubyte free = findNextFree(values);
|
||||
assert(isPresent(values, free) == false);
|
||||
}
|
Loading…
Reference in New Issue