My thread-safe queue code appears to work, any possible race conditions, deadlocks, or other design problems? - multithreading

I am new to using condition_variables and unique_locks in C++. I am working on creating an event loop that polls two custom event-queues and a "boolean" (see integer acting as boolean), which can be acted upon by multiple sources.
I have a demo (below) that appears to work, which I would greatly appreciate if you can review and confirm if it follows the best practices for using unique_lock and condition_variables and any problems you foresee happening (race conditions, thread blocking, etc).
In ThreadSafeQueue::enqueue(...): are we unlocking twice by calling notify and having the unique_lock go out of scope?
In the method TheadSafeQueue::dequeueAll(): We assume it is being called by a method that has been notified (cond.notify), and therefore has been locked. Is there a better way to encapsulate this to keep the caller cleaner?
Do we need to make our class members volatile similar to this?
Is there a better way to mockup our situation that allows us to test if we've correctly implemented the locks? Perhaps without the sleep statements and automating the checking process?
ThreadSafeQueue.h:
#include <condition_variable>
#include <cstdint>
#include <iostream>
#include <mutex>
#include <vector>
template <class T>
class ThreadSafeQueue {
public:
ThreadSafeQueue(std::condition_variable* cond, std::mutex* unvrsl_m)
: ThreadSafeQueue(cond, unvrsl_m, 1) {}
ThreadSafeQueue(std::condition_variable* cond, std::mutex* unvrsl_m,
uint32_t capacity)
: cond(cond),
m(unvrsl_m),
head(0),
tail(0),
capacity(capacity),
buffer((T*)malloc(get_size() * sizeof(T))),
scratch_space((T*)malloc(get_size() * sizeof(T))) {}
std::condition_variable* cond;
~ThreadSafeQueue() {
free(scratch_space);
free(buffer);
}
void resize(uint32_t new_cap) {
std::unique_lock<std::mutex> lock(*m);
check_params_resize(new_cap);
free(scratch_space);
scratch_space = buffer;
buffer = (T*)malloc(sizeof(T) * new_cap);
copy_cyclical_queue();
free(scratch_space);
scratch_space = (T*)malloc(new_cap * sizeof(T));
tail = get_size();
head = 0;
capacity = new_cap;
}
void enqueue(const T& value) {
std::unique_lock<std::mutex> lock(*m);
resize();
buffer[tail++] = value;
if (tail == get_capacity()) {
tail = 0;
} else if (tail > get_capacity())
throw("Something went horribly wrong TSQ: 75");
cond->notify_one();
}
// Assuming m has already been locked by the caller...
void dequeueAll(std::vector<T>* vOut) {
if (get_size() == 0) return;
scratch_space = buffer;
copy_cyclical_queue();
vOut->insert(vOut->end(), buffer, buffer + get_size());
head = tail = 0;
}
// Const functions because they shouldn't be modifying the internal variables
// of the object
bool is_empty() const { return get_size() == 0; }
uint32_t get_size() const {
if (head == tail)
return 0;
else if (head < tail) {
// 1 2 3
// 0 1 2
// 1
// 0
return tail - head;
} else {
// 3 _ 1 2
// 0 1 2 3
// capacity-head + tail+1 = 4-2+0+1 = 2 + 1
return get_capacity() - head + tail + 1;
}
}
uint32_t get_capacity() const { return capacity; }
//---------------------------------------------------------------------------
private:
std::mutex* m;
uint32_t head;
uint32_t tail;
uint32_t capacity;
T* buffer;
T* scratch_space;
uint32_t get_next_empty_spot();
void copy_cyclical_queue() {
uint32_t size = get_size();
uint32_t cap = get_capacity();
if (size == 0) {
return; // because we have nothing to copy
}
if (head + size <= cap) {
// _ 1 2 3 ... index = 1, size = 3, 1+3 = 4 = capacity... only need 1 copy
memcpy(buffer, scratch_space + head, sizeof(T) * size);
} else {
// 5 1 2 3 4 ... index = 1, size = 5, 1+5 = 6 = capacity... need to copy
// 1-4 then 0-1
// copy number of bytes: front = 1, to (5-1 = 4 elements)
memcpy(buffer, scratch_space + head, sizeof(T) * (cap - head));
// just copy the bytes from the front up to the first element in the old
// array
memcpy(buffer + (cap - head), scratch_space, sizeof(T) * tail);
}
}
void check_params_resize(uint32_t new_cap) {
if (new_cap < get_size()) {
std::cerr << "ThreadSafeQueue: check_params_resize: size(" << get_size()
<< ") > new_cap(" << new_cap
<< ")... data "
"loss will occur if this happens. Prevented."
<< std::endl;
}
}
void resize() {
uint32_t new_cap;
uint32_t size = get_size();
uint32_t cap = get_capacity();
if (size + 1 >= cap - 1) {
std::cout << "RESIZE CALLED --- BAD" << std::endl;
new_cap = 2 * cap;
check_params_resize(new_cap);
free(scratch_space); // free existing (too small) scratch space
scratch_space = buffer; // transfer pointer over
buffer = (T*)malloc(sizeof(T) * new_cap); // allocate a bigger buffer
copy_cyclical_queue();
// move over everything with memcpy from scratch_space to buffer
free(scratch_space); // free what used to be the too-small buffer
scratch_space =
(T*)malloc(sizeof(T) * new_cap); // recreate scratch space
tail = size;
head = 0;
// since we're done with the old array... delete for memory management->
capacity = new_cap;
}
}
};
// Event Types
// keyboard/mouse
// network
// dirty flag
Main.cpp:
#include <unistd.h>
#include <cstdint>
#include <iostream>
#include <mutex>
#include <queue>
#include <sstream>
#include <thread>
#include "ThreadSafeQueue.h"
using namespace std;
void write_to_threadsafe_queue(ThreadSafeQueue<uint32_t> *q,
uint32_t startVal) {
uint32_t count = startVal;
while (true) {
q->enqueue(count);
cout << "Successfully enqueued: " << count << endl;
count += 2;
sleep(count);
}
}
void sleep_and_set_redraw(int *redraw, condition_variable *cond) {
while (true) {
sleep(3);
__sync_fetch_and_or(redraw, 1);
cond->notify_one();
}
}
void process_events(vector<uint32_t> *qOut, condition_variable *cond,
ThreadSafeQueue<uint32_t> *q1,
ThreadSafeQueue<uint32_t> *q2, int *redraw, mutex *m) {
while (true) {
unique_lock<mutex> lck(*m);
cond->wait(lck);
q1->dequeueAll(qOut);
q2->dequeueAll(qOut);
if (__sync_fetch_and_and(redraw, 0)) {
cout << "FLAG SET" << endl;
qOut->push_back(0);
}
for (auto a : *qOut) cout << a << "\t";
cout << endl;
cout << "PROCESSING: " << qOut->size() << endl;
qOut->clear();
}
}
void test_2_queues_and_bool() {
try {
condition_variable cond;
mutex m;
ThreadSafeQueue<uint32_t> q1(&cond, &m, 1024);
ThreadSafeQueue<uint32_t> q2(&cond, &m, 1024);
int redraw = 0;
vector<uint32_t> qOut;
thread t1(write_to_threadsafe_queue, &q1, 2);
thread t2(write_to_threadsafe_queue, &q2, 1);
thread t3(sleep_and_set_redraw, &redraw, &cond);
thread t4(process_events, &qOut, &cond, &q1, &q2, &redraw, &m);
t1.join();
t2.join();
t3.join();
t4.join();
} catch (system_error &e) {
cout << "MAIN TEST CRASHED" << e.what();
}
}
int main() { test_2_queues_and_bool(); }

Related

synchronize 3 threads to print sequential output

I was asked this question in an interview. I was pretty clueless.
So I decided to learn some multithreading and hopefully find an answer to this question.
I need to use 3 threads to print the output: 01020304050607.....
Thread1: prints 0
Thread2: prints odd numbers
Thread3: prints even numbers
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
std::mutex m;
std::condition_variable cv1, cv2, cv3;
int count = 0;
void printzero(int end)
{
while (count <= end)
{
std::unique_lock<std::mutex> lock(m);
cv1.wait(lock);
std::cout << 0 << " ";
++count;
if (count % 2 == 1)
{
lock.unlock();
cv2.notify_one();
}
else
{
lock.unlock();
cv3.notify_one();
}
}
}
void printodd(int end)
{
while (count <= end)
{
std::unique_lock<std::mutex> lock(m);
cv2.wait(lock);
if (count % 2 == 1)
{
std::cout << count << " ";
++count;
lock.unlock();
cv1.notify_one();
}
}
}
void printeven(int end)
{
while (count <= end)
{
std::unique_lock<std::mutex> lock(m);
cv3.wait(lock);
if (count % 2 == 0)
{
std::cout << count << " ";
++count;
lock.unlock();
cv1.notify_one();
}
}
}
int main()
{
int end = 10;
std::thread t3(printzero, end);
std::thread t1(printodd, end);
std::thread t2(printeven, end);
cv1.notify_one();
t1.join();
t2.join();
t3.join();
return 0;
}
My solution seems to be in a deadlock situation. I'm not even sure if the logic is correct. Please help
There are several issues with your code. Here is what you need to do in order to make it work:
Revise your while (count <= end) check. Reading count without synchronization is undefined behavior (UB).
Use a proper predicate with std::condition_variable::wait. Problems of your code without predicate:
If notify_one is called before wait then the notification is lost. In the worst case, main's call to notify_one is executed before the threads start running. As a result, all threads may wait indefinitely.
Spurious wakeups may disrupt your program flow. See also cppreference.com on std::condition variable.
Use std::flush (just to be sure).
I played around with your code quite a lot. Below you find a version where I applied my suggested fixes. In addition, I also experimented with some other ideas that came to my mind.
#include <cassert>
#include <condition_variable>
#include <functional>
#include <iostream>
#include <mutex>
#include <thread>
#include <vector>
// see the `std::mutex` for an example how to avoid global variables
std::condition_variable cv_zero{};
std::condition_variable cv_nonzero{};
bool done = false;
int next_digit = 1;
bool need_zero = true;
void print_zero(std::mutex& mt) {
while(true) {// do not read shared state without holding a lock
std::unique_lock<std::mutex> lk(mt);
auto pred = [&] { return done || need_zero; };
cv_zero.wait(lk, pred);
if(done) break;
std::cout << 0 << "\t"
<< -1 << "\t"// prove that it works
<< std::this_thread::get_id() << "\n"// prove that it works
<< std::flush;
need_zero = false;
lk.unlock();
cv_nonzero.notify_all();// Let the other threads decide which one
// wants to proceed. This is probably less
// efficient, but preferred for
// simplicity.
}
}
void print_nonzero(std::mutex& mt, int end, int n, int N) {
// Example for `n` and `N`: Launch `N == 2` threads with this
// function. Then the thread with `n == 1` prints all odd numbers, and
// the one with `n == 0` prints all even numbers.
assert(N >= 1 && "number of 'nonzero' threads must be positive");
assert(n >= 0 && n < N && "rank of this nonzero thread must be valid");
while(true) {// do not read shared state without holding a lock
std::unique_lock<std::mutex> lk(mt);
auto pred = [&] { return done || (!need_zero && next_digit % N == n); };
cv_nonzero.wait(lk, pred);
if(done) break;
std::cout << next_digit << "\t"
<< n << "\t"// prove that it works
<< std::this_thread::get_id() << "\n"// prove that it works
<< std::flush;
// Consider the edge case of `end == INT_MAX && next_digit == INT_MAX`.
// -> You need to check *before* incrementing in order to avoid UB.
assert(next_digit <= end);
if(next_digit == end) {
done = true;
cv_zero.notify_all();
cv_nonzero.notify_all();
break;
}
++next_digit;
need_zero = true;
lk.unlock();
cv_zero.notify_one();
}
}
int main() {
int end = 10;
int N = 2;// number of threads for `print_nonzero`
std::mutex mt{};// example how to pass by reference (avoiding globals)
std::thread t_zero(print_zero, std::ref(mt));
// Create `N` `print_nonzero` threads with `n` in [0, `N`).
std::vector<std::thread> ts_nonzero{};
for(int n=0; n<N; ++n) {
// Note that it is important to pass `n` by value.
ts_nonzero.emplace_back(print_nonzero, std::ref(mt), end, n, N);
}
t_zero.join();
for(auto&& t : ts_nonzero) {
t.join();
}
}

How to write to multiple files on different disks simultaneously in one thread with DMA?

I use aio to write multiple files on different disk in one thread. When I use buffered writing, IO processing is concurrent. But cpu loads is very high. When I open files with DIRECT flag, IO processing isn't concurrent.
How to write to multiple files on different disks simultaneously in one thread with DMA?
#include <malloc.h>
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <sstream>
#include <inttypes.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/syscall.h>
#include <linux/aio_abi.h>
using namespace std;
long double timeDiff(timespec start, timespec end) {
const long double s = start.tv_sec + start.tv_nsec * 1.0e-9;
const long double e = end.tv_sec + end.tv_nsec * 1.0e-9;
return e - s;
}
// nr: maximum number of requests that can simultaneously reside in the context.
inline int io_setup(unsigned nr, aio_context_t *ctxp) {
return syscall(__NR_io_setup, nr, ctxp);
}
inline int io_destroy(aio_context_t ctx) {
return syscall(__NR_io_destroy, ctx);
}
// Every I/O request that is submitted to
inline int io_submit(aio_context_t ctx, long nr, struct iocb **iocbpp) {
return syscall(__NR_io_submit, ctx, nr, iocbpp);
}
// For every completed I/O request kernel creates an io_event structure.
// minimal number of events one wants to get.
// maximum number of events one wants to get.
inline int io_getevents(aio_context_t ctx, long min_nr, long max_nr,
struct io_event *events, struct timespec *timeout) {
return syscall(__NR_io_getevents, ctx, min_nr, max_nr, events, timeout);
}
int main(int argc, char *argv[]) {
// prepare data
const unsigned int kAlignment = 4096;
const long data_size = 1600 * 1024 * 12 / 8;
//const long data_size = 2448 * 1344 * 12 / 8;
void * data = memalign(kAlignment, data_size);
memset(data, 0, data_size);
//for (int i = 0; i < data_size; ++i)
// data[i] = 'A';
// prepare fd
//const int file_num = 3;
const int file_num = 2;
int fd_arr[file_num];
for (int i = 0; i < file_num; ++i) {
ostringstream filename;
if (i == 0) {
//filename << "/data/test";
filename << "/test";
} else {
filename << "/data" << i << "/test";
}
//filename << "/data/test" << i;
int fd = open(filename.str().c_str(), O_WRONLY | O_NONBLOCK | O_CREAT | O_DIRECT | O_APPEND, 0644);
//int fd = open(filename.str().c_str(), O_WRONLY | O_NONBLOCK | O_CREAT | O_DIRECT, 0644);
//int fd = open(filename.str().c_str(), O_WRONLY | O_NONBLOCK | O_CREAT, 0644);
if (fd < 0) {
perror("open");
return -1;
}
fd_arr[i] = fd;
}
aio_context_t ctx;
struct io_event events[file_num];
int ret;
ctx = 0;
ret = io_setup(1000, &ctx);
if (ret < 0) {
perror("io_setup");
return -1;
}
struct iocb cbs[file_num];
for (int i = 0; i < file_num; ++i) {
memset(&cbs[i], 0, sizeof(cbs[i]));
}
struct iocb * cbs_pointer[file_num];
for (int i = 0; i < file_num; ++i) {
/* setup I/O control block */
cbs_pointer[i] = &cbs[i];
cbs[i].aio_fildes = fd_arr[i];
cbs[i].aio_lio_opcode = IOCB_CMD_PWRITE; // IOCV_CMD
cbs[i].aio_nbytes = data_size;
}
timespec tStart, tCurr;
clock_gettime(CLOCK_REALTIME, &tStart);
const int frame_num = 10000;
for (int k = 0; k < frame_num; ++k) {
for (int i = 0; i < file_num; ++i) {
/* setup I/O control block */
cbs[i].aio_buf = (uint64_t)data;
//cbs[i].aio_offset = k * data_size;
}
ret = io_submit(ctx, file_num, cbs_pointer);
if (ret < 0) {
perror("io_submit");
return -1;
}
/* get reply */
ret = io_getevents(ctx, file_num, file_num, events, NULL);
//printf("events: %d, k: %d\n", ret, k);
}
clock_gettime(CLOCK_REALTIME, &tCurr);
cout << "frame: " << frame_num << " time: " << timeDiff(tStart, tCurr) << endl;
ret = io_destroy(ctx);
if (ret < 0) {
perror("io_destroy");
return -1;
}
// close fd
for (int i = 0; i < file_num; ++i) {
fsync(fd_arr[i]);
close(fd_arr[i]);
}
return 0;
}
Linux can make writes actually async if and only if the physical extents being written are allocated on the disc already. Otherwise it has to take a mutex and do the allocation first, thus everything becomes synchronous.
Note that truncating the file to a new length usually doesn't actually allocate the underlying extents. You need to prewrite the contents first. Thereafter, rewriting the same extents will now be done async and thus become concurrent.
As you might be gathering, async file i/o on Linux is not great, though it keeps on getting better over time. Windows or FreeBSD have far superior implementations. Even OS X is not terrible. Use any of those instead.

how can i extract delay and throughput from manet-routing-compare.cc in ns3?

I'have started to work on ns3 and i try to extract from code manet-routing-compare.cc which is intalled by default with NS3.26 in examples/routing.I want to extract 4 parameters :number of sent packet, reception time of packet ,send time and packet size in order to calculate throughput and delay end to end.I have tried but i don't find a solution and every time we have error. Please, help me i really need this parametrs and thank you very much.
#include <fstream>
#include <iostream>
#include "ns3/core-module.h"
#include "ns3/network-module.h"
#include "ns3/internet-module.h"
#include "ns3/mobility-module.h"
#include "ns3/wifi-module.h"
#include "ns3/aodv-module.h"
#include "ns3/olsr-module.h"
#include "ns3/dsdv-module.h"
#include "ns3/dsr-module.h"
#include "ns3/applications-module.h"
using namespace ns3;
using namespace dsr;
NS_LOG_COMPONENT_DEFINE ("manet-routing-compare");
class RoutingExperiment
{
public:
RoutingExperiment ();
void Run (int nSinks, double txp, std::string CSVfileName);
//static void SetMACParam (ns3::NetDeviceContainer & devices,
// int slotDistance);
std::string CommandSetup (int argc, char **argv);
private:
Ptr<Socket> SetupPacketReceive (Ipv4Address addr, Ptr<Node> node);
void ReceivePacket (Ptr<Socket> socket);
void CheckThroughput ();
uint32_t port;
uint32_t bytesTotal;
uint32_t packetsReceived;
std::string m_CSVfileName;
int m_nSinks;
std::string m_protocolName;
double m_txp;
bool m_traceMobility;
uint32_t m_protocol;
};
RoutingExperiment::RoutingExperiment ()
: port (9),
bytesTotal (0),
packetsReceived (0),
m_CSVfileName ("manet-routing.output.csv"),
m_traceMobility (false),
m_protocol (2) // AODV
{
}
static inline std::string
PrintReceivedPacket (Ptr<Socket> socket, Ptr<Packet> packet, Address senderAddress)
{
std::ostringstream oss;
oss << Simulator::Now ().GetSeconds () << " " << socket->GetNode ()->GetId ();
if (InetSocketAddress::IsMatchingType (senderAddress))
{
InetSocketAddress addr = InetSocketAddress::ConvertFrom (senderAddress);
oss << " received one packet from " << addr.GetIpv4 ();
}
else
{
oss << " received one packet!";
}
return oss.str ();
}
void
RoutingExperiment::ReceivePacket (Ptr<Socket> socket)
{
Ptr<Packet> packet;
Address senderAddress;
while ((packet = socket->RecvFrom (senderAddress)))
{
bytesTotal += packet->GetSize ();
packetsReceived += 1;
NS_LOG_UNCOND (PrintReceivedPacket (socket, packet, senderAddress));
}
}
void
RoutingExperiment::CheckThroughput ()
{
double kbs = (bytesTotal * 8.0) / 1000;
bytesTotal = 0;
std::ofstream out (m_CSVfileName.c_str (), std::ios::app);
out << (Simulator::Now ()).GetSeconds () << ","
<< kbs << ","
<< packetsReceived << ","
<< m_nSinks << ","
<< m_protocolName << ","
<< m_txp << ""
<< std::endl;
out.close ();
packetsReceived = 0;
Simulator::Schedule (Seconds (1.0), &RoutingExperiment::CheckThroughput, this);
}
Ptr<Socket>
RoutingExperiment::SetupPacketReceive (Ipv4Address addr, Ptr<Node> node)
{
TypeId tid = TypeId::LookupByName ("ns3::UdpSocketFactory");
Ptr<Socket> sink = Socket::CreateSocket (node, tid);
InetSocketAddress local = InetSocketAddress (addr, port);
sink->Bind (local);
sink->SetRecvCallback (MakeCallback (&RoutingExperiment::ReceivePacket, this));
return sink;
}
std::string
RoutingExperiment::CommandSetup (int argc, char **argv)
{
CommandLine cmd;
cmd.AddValue ("CSVfileName", "The name of the CSV output file name", m_CSVfileName);
cmd.AddValue ("traceMobility", "Enable mobility tracing", m_traceMobility);
cmd.AddValue ("protocol", "1=OLSR;2=AODV;3=DSDV;4=DSR", m_protocol);
cmd.Parse (argc, argv);
return m_CSVfileName;
}
int
main (int argc, char *argv[])
{
RoutingExperiment experiment;
std::string CSVfileName = experiment.CommandSetup (argc,argv);
//blank out the last output file and write the column headers
std::ofstream out (CSVfileName.c_str ());
out << "SimulationSecond," <<
"ReceiveRate," <<
"PacketsReceived," <<
"NumberOfSinks," <<
"RoutingProtocol," <<
"TransmissionPower" <<
std::endl;
out.close ();
int nSinks = 10;
double txp = 7.5;
experiment.Run (nSinks, txp, CSVfileName);
}
void
RoutingExperiment::Run (int nSinks, double txp, std::string CSVfileName)
{
Packet::EnablePrinting ();
m_nSinks = nSinks;
m_txp = txp;
m_CSVfileName = CSVfileName;
int nWifis = 50;
double TotalTime = 200.0;
std::string rate ("2048bps");
std::string phyMode ("DsssRate11Mbps");
std::string tr_name ("manet-routing-compare");
int nodeSpeed = 20; //in m/s
int nodePause = 0; //in s
m_protocolName = "protocol";
Config::SetDefault ("ns3::OnOffApplication::PacketSize",StringValue ("64"));
Config::SetDefault ("ns3::OnOffApplication::DataRate", StringValue (rate));
//Set Non-unicastMode rate to unicast mode
Config::SetDefault ("ns3::WifiRemoteStationManager::NonUnicastMode",StringValue (phyMode));
NodeContainer adhocNodes;
adhocNodes.Create (nWifis);
// setting up wifi phy and channel using helpers
WifiHelper wifi;
wifi.SetStandard (WIFI_PHY_STANDARD_80211b);
YansWifiPhyHelper wifiPhy = YansWifiPhyHelper::Default ();
YansWifiChannelHelper wifiChannel;
wifiChannel.SetPropagationDelay ("ns3::ConstantSpeedPropagationDelayModel");
wifiChannel.AddPropagationLoss ("ns3::FriisPropagationLossModel");
wifiPhy.SetChannel (wifiChannel.Create ());
// Add a mac and disable rate control
WifiMacHelper wifiMac;
wifi.SetRemoteStationManager ("ns3::ConstantRateWifiManager",
"DataMode",StringValue (phyMode),
"ControlMode",StringValue (phyMode));
wifiPhy.Set ("TxPowerStart",DoubleValue (txp));
wifiPhy.Set ("TxPowerEnd", DoubleValue (txp));
wifiMac.SetType ("ns3::AdhocWifiMac");
NetDeviceContainer adhocDevices = wifi.Install (wifiPhy, wifiMac, adhocNodes);
MobilityHelper mobilityAdhoc;
int64_t streamIndex = 0; // used to get consistent mobility across scenarios
ObjectFactory pos;
pos.SetTypeId ("ns3::RandomRectanglePositionAllocator");
pos.Set ("X", StringValue ("ns3::UniformRandomVariable[Min=0.0|Max=300.0]"));
pos.Set ("Y", StringValue ("ns3::UniformRandomVariable[Min=0.0|Max=1500.0]"));
Ptr<PositionAllocator> taPositionAlloc = pos.Create ()->GetObject<PositionAllocator> ();
streamIndex += taPositionAlloc->AssignStreams (streamIndex);
std::stringstream ssSpeed;
ssSpeed << "ns3::UniformRandomVariable[Min=0.0|Max=" << nodeSpeed << "]";
std::stringstream ssPause;
ssPause << "ns3::ConstantRandomVariable[Constant=" << nodePause << "]";
mobilityAdhoc.SetMobilityModel ("ns3::RandomWaypointMobilityModel",
"Speed", StringValue (ssSpeed.str ()),
"Pause", StringValue (ssPause.str ()),
"PositionAllocator", PointerValue (taPositionAlloc));
mobilityAdhoc.SetPositionAllocator (taPositionAlloc);
mobilityAdhoc.Install (adhocNodes);
streamIndex += mobilityAdhoc.AssignStreams (adhocNodes, streamIndex);
AodvHelper aodv;
OlsrHelper olsr;
DsdvHelper dsdv;
DsrHelper dsr;
DsrMainHelper dsrMain;
Ipv4ListRoutingHelper list;
InternetStackHelper internet;
switch (m_protocol)
{
case 1:
list.Add (olsr, 100);
m_protocolName = "OLSR";
break;
case 2:
list.Add (aodv, 100);
m_protocolName = "AODV";
break;
case 3:
list.Add (dsdv, 100);
m_protocolName = "DSDV";
break;
case 4:
m_protocolName = "DSR";
break;
default:
NS_FATAL_ERROR ("No such protocol:" << m_protocol);
}
if (m_protocol < 4)
{
internet.SetRoutingHelper (list);
internet.Install (adhocNodes);
}
else if (m_protocol == 4)
{
internet.Install (adhocNodes);
dsrMain.Install (dsr, adhocNodes);
}
NS_LOG_INFO ("assigning ip address");
Ipv4AddressHelper addressAdhoc;
addressAdhoc.SetBase ("10.1.1.0", "255.255.255.0");
Ipv4InterfaceContainer adhocInterfaces;
adhocInterfaces = addressAdhoc.Assign (adhocDevices);
OnOffHelper onoff1 ("ns3::UdpSocketFactory",Address ());
onoff1.SetAttribute ("OnTime", StringValue ("ns3::ConstantRandomVariable[Constant=1.0]"));
onoff1.SetAttribute ("OffTime", StringValue ("ns3::ConstantRandomVariable[Constant=0.0]"));
for (int i = 0; i < nSinks; i++)
{
Ptr<Socket> sink = SetupPacketReceive (adhocInterfaces.GetAddress (i), adhocNodes.Get (i));
AddressValue remoteAddress (InetSocketAddress (adhocInterfaces.GetAddress (i), port));
onoff1.SetAttribute ("Remote", remoteAddress);
Ptr<UniformRandomVariable> var = CreateObject<UniformRandomVariable> ();
ApplicationContainer temp = onoff1.Install (adhocNodes.Get (i + nSinks));
temp.Start (Seconds (var->GetValue (100.0,101.0)));
temp.Stop (Seconds (TotalTime));
}
std::stringstream ss;
ss << nWifis;
std::string nodes = ss.str ();
std::stringstream ss2;
ss2 << nodeSpeed;
std::string sNodeSpeed = ss2.str ();
std::stringstream ss3;
ss3 << nodePause;
std::string sNodePause = ss3.str ();
std::stringstream ss4;
ss4 << rate;
std::string sRate = ss4.str ();
//NS_LOG_INFO ("Configure Tracing.");
//tr_name = tr_name + "_" + m_protocolName +"_" + nodes + "nodes_" + sNodeSpeed + "speed_" + sNodePause + "pause_" + sRate + "rate";
//AsciiTraceHelper ascii;
//Ptr<OutputStreamWrapper> osw = ascii.CreateFileStream ( (tr_name + ".tr").c_str());
//wifiPhy.EnableAsciiAll (osw);
AsciiTraceHelper ascii;
MobilityHelper::EnableAsciiAll (ascii.CreateFileStream (tr_name + ".mob"));
//Ptr<FlowMonitor> flowmon;
//FlowMonitorHelper flowmonHelper;
//flowmon = flowmonHelper.InstallAll ();
NS_LOG_INFO ("Run Simulation.");
CheckThroughput ();
Simulator::Stop (Seconds (TotalTime));
Simulator::Run ();
//flowmon->SerializeToXmlFile ((tr_name + ".flowmon").c_str(), false, false);
Simulator::Destroy ();
}
I am gonna answer it in two parts,
First the parameters which you can get without making much changes, which are reception time and packet size:
PrintReceivedPacket function is called when a packet is received at the receiver, you have access to received packet at this point. The Simulator::Now () gives you the reception time of the packet at sink and packet->GetSize () gives you the size of packet received.
Second the parameters of send time and number of packet sent. You will have to add a sequence to steps to get the trace to
Tx of OnOffApplication
Get Application from ApplicationContainer temp using Get (0)
Cast Application to OnOffApplication using dynamic cast
Connect to the Tx of this pointer to a function and in this function increase a global counter for number of packet sent and log the current time for sent time
Do let me know if any query

Share std::map between processes with mmap()

I'm trying to share a std::map<std::string, std::chrono::system_clock::time_point> map: each string is a hostname identifying a site, and the time_point is the last time a process visited that site.
I was trying with mmap but each process still see its own copy of the map.
Here's my code (I took away all the methods and variables not concerning my problem):
#include <sys/mman.h>
#include <unistd.h>
#include <iostream>
#include <map>
#include <string>
#include <chrono>
typedef std::map<std::string, std::chrono::system_clock::time_point> mymap;
typedef mymap::iterator iter;
typedef mymap* mapPointer;
class MmapManager {
private:
MmapManager() {
frequency = (mapPointer) mmap(NULL, sizeof(frequency), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
if (frequency == MAP_FAILED) {
std::cout << "mapping failed" << std::endl;
}
};
~MmapManager() {
std::cout << "~MmapManager()" << std::endl;
}
public:
// my class was designed with the singleton pattern
static MmapManager& getInstance() {
static MmapManager instance;
return instance;
}
private:
// pointer to my map
mapPointer frequency;
public:
// check if the process already visited site "host"
bool isHostAlreadyVisited(std::string host) {
return frequency->find(host) != frequency->end();
}
// add new visited site and time of the visit
void addHost(std::string host) {
(*frequency)[host] = std::chrono::system_clock::now();
std::cout << "PROC " << getpid() << " added " << host << std::endl;
}
// get time of the visit for site "host"
std::chrono::system_clock::time_point getElement(std::string host) {
return (*frequency)[host];
}
// print the map
void showMap(void) {
std::cout << "PROC " << getpid() << " prints map keys" << std::endl;
for (auto it = frequency->begin(); it != frequency->end(); ++it) {
std::cout << it->first << std::endl;
}
}
};
int main(void) {
// simulate the processes
for (int i=0; i<5; i++) {
// child process
if (fork() == 0) {
// if child never visited this site...
if (! MmapManager::getInstance().isHostAlreadyVisited("www.google.com")) {
std::cout << "PID " << getpid() << " www.google.com is new" << std::endl;
// ...add it to the map
MmapManager::getInstance().addHost("www.google.com");
}
else {
// if child already visited it, calculate
// how much time passed since last visit
auto now = std::chrono::system_clock::now();
auto before = MmapManager::getInstance().getElement("www.google.com");
std::chrono::duration<double> diff = now-before;
std::cout << "PID " << getpid() << " visited www.google.com " << diff.count() << " seconds ago" << std::endl;
}
MmapManager::getInstance().showMap();
_exit(EXIT_SUCCESS);
}
}
return 0;
}
Here's one of the possible outputs:
PID 12457 www.google.com is new
PID 12459 www.google.com is new
PID 12458 www.google.com is new
PID 12460 www.google.com is new
PID 12461 www.google.com is new
I can't use other external libraries like Boost or use threads: I know they share memory, but the program was designed this way (with child processes doing stuff) and I can't modify it (original code is not mine).
Why does each process still see its own copy of the map?
Edit: I think I did all the things you suggested me:
insertion in map is protected with a lock mechanism (thanks kfsone);
created a custom allocator for string and another for map (thanks Maxim Egorushkin for these two);
map is allocated before forking (thanks Zan Lynx).
The output is not different and map is still not shared:
MmapManager()
printMap
map empty
PID 5085 www.google.com is new
PID 5086 www.google.com is new
PROC 5086 added www.goole.com
PROC 5085 added www.goole.com
PID 5087 www.google.com is new
PROC 5087 added www.goole.com
You suggested me to use Boost but I'd like to use it after my code will work: I'm not reinventing the wheel, just learning the hard way.
Here follows my new code:
#include <sys/mman.h>
#include <unistd.h>
#include <sys/shm.h> /* shmat(), IPC_RMID */
#include <semaphore.h> /* sem_open(), sem_destroy(), sem_wait().. */
#include <fcntl.h> /* O_CREAT, O_EXEC */
#include <stdlib.h>
#include <iostream>
#include <map>
#include <string>
#include <chrono>
#include <cstddef>
#include <vector>
#include <limits>
#include <memory>
template<typename T> class stringAllocator {
public :
typedef T value_type;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef T* pointer;
typedef T const * const_pointer;
typedef T& reference;
typedef T const & const_reference;
template<typename U> struct rebind {
typedef stringAllocator<U> other;
};
pointer address (reference value ) const {
return &value;
}
const_pointer address (const_reference value) const {
return &value;
}
size_type max_size () const throw() {
return std::numeric_limits <size_type>::max() / sizeof(T);
}
stringAllocator () throw () {}
stringAllocator (stringAllocator const &) throw () {}
template <typename U>
stringAllocator(stringAllocator <U> const &) throw () {}
~stringAllocator() throw () {}
pointer allocate (size_type n) {
pointer ptr = (pointer)malloc(n * sizeof(value_type));
return ptr;
}
void deallocate (pointer p, size_type n) {
free(p);
}
void construct (pointer p, const_reference value) {
new(p) T(value);
}
void destroy (pointer p) {
p->~T();
}
};
template <class T1, class T2>
bool operator==(const stringAllocator<T1>&, const stringAllocator<T2>&) throw() {
return true;
}
template <class T1, class T2>
bool operator!=(const stringAllocator<T1>&, const stringAllocator<T2>&) throw() {
return false;
}
typedef std::basic_string<
char,
std::char_traits<char>,
stringAllocator<char>
> myString;
/*************************************** map allocator ****************************************/
template<typename T> class mapAllocator{
public :
typedef T value_type;
typedef value_type* pointer;
typedef const value_type* const_pointer;
typedef value_type& reference;
typedef const value_type& const_reference;
typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type;
template<typename U>
struct rebind {
typedef mapAllocator<U> other;
};
mapAllocator() throw() {}
mapAllocator (mapAllocator const &) throw () {}
~mapAllocator() throw () {}
template<typename U>
mapAllocator(mapAllocator<U> const&) {}
pointer address(reference r) { return &r; }
const_pointer address(const_reference r) { return &r; }
pointer allocate(size_type cnt, typename std::allocator<void>::const_pointer = 0) {
pointer new_memory = reinterpret_cast<pointer>(::operator new(cnt * sizeof (T)));
return new_memory;
}
void deallocate(pointer p, size_type n) {
::operator delete(p);
}
size_type max_size() const {
return std::numeric_limits<size_type>::max() / sizeof(T);
}
void construct(pointer p, const T& t) {
new(p) T(t);
}
void destroy(pointer p) {
p->~T();
}
};
template <class T1, class T2>
bool operator==(const mapAllocator<T1>&, const mapAllocator<T2>&) throw() {
return true;
}
template <class T1, class T2>
bool operator!=(const mapAllocator<T1>&, const mapAllocator<T2>&) throw() {
return false;
}
/*************************************** end map allocator ****************************************/
// class compare for map with std::string as Key
class strless {
public:
bool operator() (const myString first, const myString second ) const {
return first.compare(second) < 0;
}
};
template<typename Key, typename T>
using Map = std::map<
Key, // class Key
T, // class T
strless, // class Compare = std::less<Key>
mapAllocator<std::pair<const Key, T> // class Allocator = std::allocator<std::pair<const Key, T> >
>
>;
// typedef for the actual map I need to share between processes
typedef Map<myString, std::chrono::system_clock::time_point> frequencyMap;
class MmapManager {
private:
MmapManager() {
std::cout << "MmapManager()" << std::endl;
semMmap = sem_open("semaphore", O_CREAT|O_EXCL, 0644, 1);
sem_unlink("semaphore");
};
~MmapManager() {
std::cout << "~MmapManager()" << std::endl;
}
public:
static MmapManager& getInstance() {
static MmapManager instance;
return instance;
}
private:
frequencyMap fmap;
sem_t *semMmap;
public:
void start(void) {}
bool isHostAlreadyVisited(myString host) {
return fmap.find(host) != fmap.end();
}
void addHost(myString host) {
sem_wait(semMmap);
fmap[host] = std::chrono::system_clock::now();
sem_post(semMmap);
std::cout << "PROC " << getpid() << " added " << host << std::endl;
}
// get time of the visit for site "host"
std::chrono::system_clock::time_point getElement(myString host) {
return fmap[host];
}
void printMap(void) {
std::cout << "printMap" << std::endl;
if (!fmap.empty()) {
for (auto it : fmap) {
std::cout << it.first << ' ';
}
std::cout << std::endl;
} else {
std::cout << "map empty" << std::endl;
}
}
};
int main(void) {
MmapManager::getInstance().start();
for (int i=0; i<3; i++) {
if (fork() == 0) {
if (!MmapManager::getInstance().isHostAlreadyVisited("www.google.com")) {
std::cout << "PID " << getpid() << " www.google.com is new" << std::endl;
MmapManager::getInstance().addHost("www.goole.com");
}
else {
// if child already visited it, calculate
// how much time passed since last visit
auto now = std::chrono::system_clock::now();
auto before = MmapManager::getInstance().getElement("www.google.com");
std::chrono::duration<double> diff = now-before;
std::cout << "PID " << getpid() << " visited www.google.com " << diff.count() << " seconds ago" << std::endl;
}
_exit(EXIT_SUCCESS);
}
}
MmapManager::getInstance().printMap();
return 0;
}
This does not work because although you placed the container object into the shared memory, the elements are still allocated from the heap and thus they are not accessible by other processes.
You need a custom allocator that allocates elements in the shared memory. See Creating maps in shared memory for how it is done.
Note that the string class you use must also allocate memory from the shared memory.
In other words, you cannot have pointers to heap memory in the shared memory, because heap memory is not shared between processes. std classes have an allocator template argument, the default one allocates memory from the heap. This needs to be changed to a shared memory allocator to be able to share such objects via shared memory.
Another reason your code doesn't work is that you only create the maps after you called fork().
If you want your MAP_SHARED|MAP_ANONYMOUS map to be seen by all the children then you have to call mmap() before forking.

VC++ threads deadlocked

The following program goes into a deadlock. Can anyone please tell me why?
#include<cstdlib>
#include<windows.h>
#include<iostream>
using namespace std;
class CircularQueue
{
public:
CircularQueue(int s)
{
size = s;
array = (int*)malloc(sizeof(int) * size);
head = tail = -1;
InitializeCriticalSection(&critical_section);
}
CircularQueue(void)
{
size = default_size;
array = (int*)malloc(sizeof(int) * size);
head = tail = -1;
InitializeCriticalSection(&critical_section);
}
void initialize(int s)
{
EnterCriticalSection(&critical_section);
size = s;
array = (int*)realloc(array, sizeof(int) * size);
head = tail = -1;
LeaveCriticalSection(&critical_section);
}
void enqueue(int n)
{
EnterCriticalSection(&critical_section);
tail = (tail + 1) % size;
array[tail] = n;
LeaveCriticalSection(&critical_section);
}
int dequeue(void)
{
EnterCriticalSection(&critical_section);
head = (head + 1) % size;
return array[head];
LeaveCriticalSection(&critical_section);
}
private:
int *array;
int size;
int head, tail;
CRITICAL_SECTION critical_section;
bool initialized;
static const int default_size = 10;
};
DWORD WINAPI thread1(LPVOID param)
{
CircularQueue* cqueue = (CircularQueue*)param;
cqueue->enqueue(2);
cout << cqueue->dequeue() << endl;
return 0;
}
DWORD WINAPI thread2(LPVOID param)
{
CircularQueue* cqueue = (CircularQueue*)param;
cqueue->enqueue(3);
cout << cqueue->dequeue() << endl;
return 0;
}
int main(void)
{
HANDLE thread1_handle;
HANDLE thread2_handle;
CircularQueue cqueue;
HANDLE array[2];
thread1_handle = CreateThread(NULL, 0, thread1, &cqueue, 0, NULL);
thread2_handle = CreateThread(NULL, 0, thread2, &cqueue, 0, NULL);
array[0] = thread1_handle;
array[1] = thread2_handle;
WaitForMultipleObjects(1, array, TRUE, INFINITE);
CloseHandle(thread1_handle);
CloseHandle(thread2_handle);
printf("end\n");
return 0;
}
In dequeue(), you have a return statement before the LeaveCriticalSection() call. If you had compiler warnings turned up higher, it would probably have told you about this!

Resources