void *switch_thread_routine(void *arg) { int i, k; // For loops while (!die) { // Check if the in ports have any pending packets for (i = 0; i < 4; i++) { port_lock(&(in_port[i])); if (in_port[i].flag) { // If flag is set, deal with the packet int target_port = cam_lookup_address(&in_port[i].packet.address); port_lock(&(out_port[target_port])); // If the target out port is free, send the packet to it. // Else, add the current packet to the port's buffer. if (!out_port[target_port].flag) { out_port[target_port].packet.address = in_port[i].packet.address; out_port[target_port].packet.payload = in_port[i].packet.payload; out_port[target_port].flag = 1; } else { // If the buffer isn't full, add the packet. // Packet will be lost if the buffer is full. if (pending[target_port] < BUFFER_SIZE) { buffers[target_port][++pending[target_port]].address = in_port[i].packet.address; buffers[target_port][pending[target_port]].payload = in_port[i].packet.payload; } } in_port[i].flag = 0; port_unlock(&(out_port[target_port])); } port_unlock(&(in_port[i])); } // Check if the out ports have any pending packets in their buffers for (k = 0; k < 4; k++) { port_lock(&(out_port[k])); // If the output port is free and has packets in its buffer, send pcaktes to the port. if (!out_port[k].flag && pending[k] >= 0) { out_port[k].packet.address = buffers[k][pending[k]].address; out_port[k].packet.payload = buffers[k][pending[k]].payload; out_port[k].flag = 1; pending[k]--; } port_unlock(&(out_port[k])); } } }
static void connect_port_callback(PortConnection * conn, int error) { assert(is_dispatch_thread()); port_unlock(conn); if (conn->shutdown_in_progress) return; if (error != 0) { port_connection_close(conn); return; } else { int idx; if (conn->server->connect_callback) conn->server->connect_callback(conn->server, conn->server->callback_data); conn->connected = 1; if (conn->fd != -1) { port_lock(conn); conn->recv_req.u.sio.sock = conn->fd; conn->recv_req.u.sio.addr = &conn->server->client_addr; conn->recv_req.u.sio.addrlen = sizeof(conn->server->client_addr); async_req_post(&conn->recv_req); } /* Send multiple TCF streams read requests in parallel; this is * to limit the performance impact on network with high latency. */ for (idx = 0; idx < MAX_STREAM_READ; idx++) read_packet(conn, idx); } }
static void done_recv_request(void * args) { AsyncReqInfo * req = (AsyncReqInfo *) args; PortConnection * conn = (PortConnection *) (req)->client_data; port_unlock(conn); if (conn->connected == 0) { port_connection_close(conn); return; } if (req->u.sio.rval == 0 || (req->u.sio.rval == -1 && req->error != EINTR)) { /* Check if we are in auto connect mode and server has not been * shut down */ if (conn->server->auto_connect && conn->server->sock != -1) { /* Client has disconnected; don't close the connection if we * are in auto connect mode but simply unbind the client from * the port. */ port_connection_unbind(conn); } else port_connection_close(conn); return; } port_lock(conn); conn->server->client_addr_len = req->u.sio.addrlen; send_packet(conn, req->u.sio.bufp, req->u.sio.rval); }
void chSysLock(void) { chDbgAssert(currp->p_locks >= 0, "chSysLock(), #1", "negative nesting counter"); if (currp->p_locks++ == 0) port_lock(); }
static void done_send_request(void * args) { AsyncReqInfo * req = (AsyncReqInfo *) args; PortConnection * conn = (PortConnection *) (req)->client_data; int idx = conn->send_in_progress; port_unlock(conn); conn->send_in_progress = -1; if (conn->connected == 0) { port_connection_close(conn); return; } if (req->u.sio.rval == 0 || (req->u.sio.rval == -1 && req->error != EINTR)) { /* Check if we are in auto connect mode and server has not been * shutdown */ if (conn->server->auto_connect && conn->server->sock != -1) { /* Client has disconnected; don't close the connection if we * are in auto connect mode but simply unbind the client from * the port. */ port_connection_unbind(conn); /* Still read packets from the target even if no client is * connected. This may have to be revisited. */ read_packet(conn, idx); } else port_connection_close(conn); return; } if (conn->pending_send_req != 0) { int next_idx; int loop; /* Get the next packet to send. In general, it is the next buffer * but there are some error cases (connection lost, empty packet * received from TCF agent) which may break this rule. */ for (loop = 0; loop < MAX_STREAM_READ; loop++) { next_idx = (idx + loop) % MAX_STREAM_READ; if (conn->pending_send_req & (1 << next_idx)) break; } assert (loop != MAX_STREAM_READ && (conn->pending_send_req & (1 << next_idx))); conn->send_in_progress = next_idx; conn->pending_send_req &= ~(1 << next_idx); conn->send_req.u.sio.bufp = conn->read_buffer[next_idx]; conn->send_req.u.sio.bufsz = conn->read_buffer_size[next_idx]; port_lock(conn); conn->send_req.u.sio.sock = conn->fd; conn->send_req.u.sio.addr = &conn->server->client_addr; conn->send_req.u.sio.addrlen = conn->server->client_addr_len; async_req_post(&conn->send_req); } read_packet(conn, idx); }
static void disconnect_port(PortConnection * conn) { assert (is_dispatch_thread()); conn->shutdown_in_progress = 1; port_lock(conn); protocol_send_command(conn->server->channel, "PortForward", "delete", delete_config_done, conn); json_write_string(&conn->server->channel->out, conn->id); write_stream(&conn->server->channel->out, MARKER_EOA); write_stream(&conn->server->channel->out, MARKER_EOM); }
static void connect_port(PortConnection * conn) { assert(is_dispatch_thread()); sprintf(conn->id, "%s@%" PRIu64, conn->server->id, conn->server->port_index++); port_lock(conn); conn->pending = protocol_send_command(conn->server->channel, "PortForward", "getCapabilities", getcapabilities_cb, conn); write_string(&conn->server->channel->out, "null"); write_stream(&conn->server->channel->out, MARKER_EOA); write_stream(&conn->server->channel->out, MARKER_EOM); }
static int port_connection_bind(PortConnection * conn, int fd) { assert (conn->fd == -1); port_lock(conn); conn->fd = fd; conn->recv_req.u.sio.sock = conn->fd; conn->send_in_progress = -1; conn->pending_send_req = 0; conn->recv_req.u.sio.addrlen = sizeof(conn->server->client_addr); conn->recv_req.u.sio.addr = &conn->server->client_addr; async_req_post(&conn->recv_req); return 0; }
// Thread which will handle dequeing and re-enqueing based on the status // and the flags for all ports in the output buffer void *output_monitor_routine(void *arg) { packet_t in_pkt ; ip_address_t address ; int dest_port=0 ; int loop_count= 0 ; element_to_queue * element ; while(!die) { // Only care dequing if there are elements. if((output_buffer->size > 0)) { // This will indeed dequeue the packet, but we may // have to put it back if the port isn't ready. queue_lock(output_buffer) ; dequeue(output_buffer,&in_pkt) ; queue_unlock(output_buffer) ; // Fetch the IP & lookup destination port ip_address_copy(&(in_pkt.address),&address); dest_port = cam_lookup_address(&address) ; if((dest_port != -1) && (dest_port < 4)) { // Wait for the lock port_lock(&(out_port[dest_port])) ; // If the flag is busy from the last write, then // we have to put the packet back in the queue and just // have to wait until we get to it again. if(out_port[dest_port].flag) { element = calloc(1,sizeof(element_to_queue)) ; packet_copy(&in_pkt,&(element->packet)); queue_lock(output_buffer) ; enqueue(element,output_buffer) ; queue_unlock(output_buffer) ; port_unlock(&(out_port[dest_port])) ; continue ; } // Port ready to be written , so go ahead and write. packet_copy(&in_pkt,&(out_port[dest_port].packet)); out_port[dest_port].flag = TRUE ; port_unlock(&(out_port[dest_port])) ; } } // Make sure it tried to at least deque 5 elements, before we // make it sleep. if(loop_count > LOOP_COUNT) { loop_count = 0 ; sleep() ; } else loop_count++ ; } }
static void send_packet_callback(PortConnection * conn, int error) { assert(is_dispatch_thread()); port_unlock(conn); if (error != 0) { port_connection_close(conn); } else { port_lock(conn); conn->recv_req.u.sio.sock = conn->fd; conn->recv_req.u.sio.addrlen = sizeof(conn->server->client_addr); async_req_post(&conn->recv_req); } }
/** * @return TRUE if already in locked context */ bool lockAnyContext(void) { int alreadyLocked = isLocked(); if (alreadyLocked) return true; #if USE_PORT_LOCK port_lock(); #else /* #if USE_PORT_LOCK */ if (isIsrContext()) { chSysLockFromISR() ; } else { chSysLock() ; } #endif /* #if USE_PORT_LOCK */ return false; }
static void read_packet_callback(PortConnection * conn, int error, int idx, size_t size) { assert(is_dispatch_thread()); port_unlock(conn); if (error != 0 || size == 0) { port_connection_close(conn); } else { conn->read_buffer_size[idx] = size; /* Call read hooks if any. Note that those hooks can modify the content of the * packets (remove characters). */ if (conn->server->recv_callback) { conn->server->recv_callback(conn->server, conn->read_buffer[idx], &conn->read_buffer_size[idx], IN_BUF_SIZE, conn->server->callback_data); } /* If no client is connected or if the filter has removed all the packet content, * do not post a send request. */ if (conn->fd != -1 && conn->read_buffer_size[idx] != 0) { /* If there is already a send progress in request; postpone the * current one until it is completed. */ if (conn->send_in_progress != -1) { conn->pending_send_req |= 1 << idx; return; } port_lock(conn); conn->send_in_progress = idx; assert (conn->pending_send_req == 0); conn->send_req.u.sio.bufp = conn->read_buffer[idx]; conn->send_req.u.sio.bufsz = conn->read_buffer_size[idx]; conn->send_req.u.sio.sock = conn->fd; conn->send_req.u.sio.addr = &conn->server->client_addr; conn->send_req.u.sio.addrlen = conn->server->client_addr_len; async_req_post(&conn->send_req); } else { read_packet(conn, idx); } } }
void *switch_thread_routine(void *arg) { BOOL recv_flag=FALSE ; packet_t in_pkt ; ip_address_t address ; int dest_port=0 ; int i= 0 ; element_to_queue * element ; while(!die) { // Lock all 4 ports first ... // This is a better way to do this, since this thread may get // preempted and switched back to the input port writer which // might override the existing messages. for(i=0 ; i < NUM_PORTS ; i++) { port_lock(&(in_port[i])) ; } // Then try to unlock and check if packets are available. // We are sure to have saved all elements in the queue once // we've unlocked them for(i=0 ; i < NUM_PORTS ; i++) { if(in_port[i].flag == TRUE) { packet_copy(&(in_port[i].packet),&in_pkt); in_port[i].flag = FALSE ; } else { port_unlock(&(in_port[i])) ; continue ; } element = calloc(1,sizeof(element_to_queue)) ; packet_copy(&in_pkt,&(element->packet)); queue_lock(output_buffer) ; enqueue(element,output_buffer) ; queue_unlock(output_buffer) ; port_unlock(&(in_port[i])) ; } // pause for a a little while and then check again sleep() ; } printf("All packets sent.. Exiting !\n") ; }
static int read_packet(PortConnection * conn, int idx) { assert (is_dispatch_thread()); assert ((conn->pending_read_request & (1 << idx)) == 0); if (conn->pending_read_request & (1 << idx)) { errno = ERR_IS_RUNNING; return -1; } port_lock(conn); conn->pending_read_request |= (1 << idx); (void) protocol_send_command(conn->server->channel, "Streams", "read", read_stream_done, &conn->read_info[idx]); json_write_string(&conn->server->channel->out, conn->in_stream_id); write_stream(&conn->server->channel->out, 0); json_write_long(&conn->server->channel->out, sizeof conn->read_buffer[idx]); write_stream(&conn->server->channel->out, MARKER_EOA); write_stream(&conn->server->channel->out, MARKER_EOM); return 0; }
/** * @brief Writes to a dedicated packet buffer. * * @param[in] udp pointer to a @p stm32_usb_descriptor_t * @param[in] buf buffer where to fetch the packet data * @param[in] n maximum number of bytes to copy. This value must * not exceed the maximum packet size for this endpoint. * * @notapi */ static void usb_packet_write_from_queue(stm32_usb_descriptor_t *udp, OutputQueue *oqp, size_t n) { size_t nhw; uint32_t *pmap = USB_ADDR2PTR(udp->TXADDR0); udp->TXCOUNT0 = (uint16_t)n; nhw = n / 2; while (nhw > 0) { uint32_t w; w = (uint32_t)*oqp->q_rdptr++; if (oqp->q_rdptr >= oqp->q_top) oqp->q_rdptr = oqp->q_buffer; w |= (uint32_t)*oqp->q_rdptr++ << 8; if (oqp->q_rdptr >= oqp->q_top) oqp->q_rdptr = oqp->q_buffer; *pmap++ = w; nhw--; } /* Last byte for odd numbers.*/ if ((n & 1) != 0) { *pmap = (uint32_t)*oqp->q_rdptr++; if (oqp->q_rdptr >= oqp->q_top) oqp->q_rdptr = oqp->q_buffer; } /* Updating queue. Note, the lock is done in this unusual way because this function can be called from both ISR and thread context so the kind of lock function to be invoked cannot be decided beforehand.*/ port_lock(); dbg_enter_lock(); oqp->q_counter += n; while (notempty(&oqp->q_waiting)) chSchReadyI(fifo_remove(&oqp->q_waiting))->p_u.rdymsg = Q_OK; dbg_leave_lock(); port_unlock(); }
// acquire locks, notify and call acknowledge_unsafe void recv_ack(unsigned char pid, unsigned int count) { // acquire port lock std::unique_lock<std::mutex> port_lock(inPorts[pid]->port_mutex); // acknowledge the data without recursive locking recv_ack_unsafe(pid, count); // if task queue is empty, notify the port cv and return if(inPorts[pid]->writeTaskQueue->empty()) { inPorts[pid]->task_empty.notify_one(); return; } // otherwise there are still values to write. // acquire writer lock std::unique_lock<std::mutex> lock(writer_mutex); // notify writer thread, if everything was acknowledged if(*inPorts[pid]->transit == 0) can_write.notify_one(); // otherwise, set the transit counter to -1 to mark a blocked port else *inPorts[pid]->transit = -1; }
void recv_poll(unsigned char pid) { // acquire port lock std::unique_lock<std::mutex> port_lock(inPorts[pid]->port_mutex); // ignore the poll, if there are unacknowledged values in transit if(*inPorts[pid]->transit > 0) return; // set transit counter to 0 to enable transmissions *inPorts[pid]->transit = 0; // notify writer thread, if there are waiting tasks if(!inPorts[pid]->writeTaskQueue->empty()) { // acquire the writer lock std::unique_lock<std::mutex> lock(writer_mutex); // release the port lock! port_lock.unlock(); // notify writer can_write.notify_one(); } }
void *switch_thread_routine(void *arg) { int inputPortNumber, outputPortNumber, i, j; // For loops int inputPortPriorities[NUM_PORTS]; int outputPortPriorities[NUM_PORTS]; // ex. virtualOutputQueues[inputPort][outputPort] packet_t virtualOutputQueues[NUM_PORTS][NUM_PORTS][VOQ_SIZE]; int virtualOutputQueueReadIndex[NUM_PORTS][NUM_PORTS]; int virtualOutputQueueWriteIndex[NUM_PORTS][NUM_PORTS]; packet_t outputBuffer[NUM_PORTS][OUTPUT_BUFFER_SIZE]; int outputBufferReadIndex[NUM_PORTS]; int outputBufferWriteIndex[NUM_PORTS]; // Initialize the buffers memset(inputPortPriorities, 0, NUM_PORTS * sizeof(int)); memset(outputPortPriorities, 0, NUM_PORTS * sizeof(int)); memset(virtualOutputQueues, 0, NUM_PORTS * NUM_PORTS * VOQ_SIZE * sizeof(packet_t)); memset(virtualOutputQueueReadIndex, 0, NUM_PORTS * NUM_PORTS * sizeof(int)); memset(virtualOutputQueueWriteIndex, 0, NUM_PORTS * NUM_PORTS * sizeof(int)); memset(outputBuffer, 0, NUM_PORTS * OUTPUT_BUFFER_SIZE * sizeof(packet_t)); memset(outputBufferReadIndex, 0, NUM_PORTS * sizeof(int)); memset(outputBufferWriteIndex, 0, NUM_PORTS * sizeof(int)); while (!die) { // Check if the in ports have any pending packets // Add them to the appropriate VOQ for (inputPortNumber = 0; inputPortNumber < NUM_PORTS; inputPortNumber++) { port_lock(&(in_port[inputPortNumber])); if (in_port[inputPortNumber].flag) { // If flag is set, deal with the packet int outputPortNumber = cam_lookup_address(&in_port[inputPortNumber].packet.address); int queueWriteIndex = virtualOutputQueueWriteIndex[inputPortNumber][outputPortNumber]; virtualOutputQueues[inputPortNumber][outputPortNumber][queueWriteIndex].address = in_port[inputPortNumber].packet.address; virtualOutputQueues[inputPortNumber][outputPortNumber][queueWriteIndex].payload = in_port[inputPortNumber].packet.payload; // Increment the write pointer for the virtual output queue virtualOutputQueueWriteIndex[inputPortNumber][outputPortNumber] = (queueWriteIndex + 1) % VOQ_SIZE; int voqwi = virtualOutputQueueWriteIndex[inputPortNumber][outputPortNumber]; // Clear the flag, we've got the packet in a virtual output queue in_port[inputPortNumber].flag = 0; } port_unlock(&(in_port[inputPortNumber])); } // Run a round of the RR-New scheduling algorithm if any of the queues are non-empty // Request phase int totalRequests = 0; // ex. requests[outputPortNumber][inputPortNumber] = TRUE int requests[NUM_PORTS][NUM_PORTS]; for (inputPortNumber = 0; inputPortNumber < NUM_PORTS; inputPortNumber++) { for (outputPortNumber = 0; outputPortNumber < NUM_PORTS; outputPortNumber++) { int queueReadIndex = virtualOutputQueueReadIndex[inputPortNumber][outputPortNumber]; int queueWriteIndex = virtualOutputQueueWriteIndex[inputPortNumber][outputPortNumber]; if (queueReadIndex != queueWriteIndex) { // Indicates the presence of a packet totalRequests++; requests[outputPortNumber][inputPortNumber] = TRUE; } else { // No request to see here... requests[outputPortNumber][inputPortNumber] = FALSE; } } } // We only need to go through the grant/accept phase if there were requests if (totalRequests > 0) { cellTimeCounter++; // Grant phase // ex. grantedRequests[inputPortNumber][outputPortNumber] = TRUE int grantedRequests[NUM_PORTS][NUM_PORTS]; for (outputPortNumber = 0; outputPortNumber < NUM_PORTS; outputPortNumber++) { // Initialize granted to FALSE for (inputPortNumber = 0; inputPortNumber < NUM_PORTS; inputPortNumber++) { grantedRequests[inputPortNumber][outputPortNumber] = FALSE; } // Start at the priority pointer, look for the first request to grant inputPortNumber = outputPortPriorities[outputPortNumber]; for (i = 0; i < NUM_PORTS; i++) { if (requests[outputPortNumber][inputPortNumber]) { grantedRequests[inputPortNumber][outputPortNumber] = TRUE; break; } inputPortNumber = (inputPortNumber + 1) % NUM_PORTS; } } // Accept phase for (inputPortNumber = 0; inputPortNumber < NUM_PORTS; inputPortNumber++) { // Start at the priority pointer, look for the first grant to accept outputPortNumber = inputPortPriorities[inputPortNumber]; for (i = 0; i < NUM_PORTS; i++) { if (grantedRequests[inputPortNumber][outputPortNumber]) { // Accept! Send the packet over int queueReadIndex = virtualOutputQueueReadIndex[inputPortNumber][outputPortNumber]; packet_t packet = virtualOutputQueues[inputPortNumber][outputPortNumber][queueReadIndex]; virtualOutputQueueReadIndex[inputPortNumber][outputPortNumber] = (queueReadIndex + 1) % VOQ_SIZE; port_lock(&(out_port[outputPortNumber])); // If the target out port is free, send the packet to it. // Else, add the current packet to the port's buffer. if (!out_port[outputPortNumber].flag) { out_port[outputPortNumber].packet.address = packet.address; out_port[outputPortNumber].packet.payload = packet.payload; out_port[outputPortNumber].flag = 1; } else { // Add the packet to the output buffer to be read later int writeIndex = outputBufferWriteIndex[outputPortNumber]; outputBuffer[outputPortNumber][writeIndex].address = packet.address; outputBuffer[outputPortNumber][writeIndex].payload = packet.payload; // Bump the write index outputBufferWriteIndex[outputPortNumber] = (writeIndex + 1) % OUTPUT_BUFFER_SIZE; } port_unlock(&(out_port[outputPortNumber])); // Update the priority trackers inputPortPriorities[inputPortNumber] = (outputPortNumber + 1) % NUM_PORTS; outputPortPriorities[outputPortNumber] = (inputPortNumber + 1) % NUM_PORTS; // We're done here... break; } // Haven't accepted anything yet, keep on tryin' outputPortNumber = (outputPortNumber + 1) % NUM_PORTS; } } } // Check if the out ports have any pending packets in their buffers for (outputPortNumber = 0; outputPortNumber < NUM_PORTS; outputPortNumber++) { port_lock(&(out_port[outputPortNumber])); // If the output port is free and has packets in its buffer, send them int readIndex = outputBufferReadIndex[outputPortNumber]; int writeIndex = outputBufferWriteIndex[outputPortNumber]; if (!out_port[outputPortNumber].flag && readIndex != writeIndex) { packet_t packet = outputBuffer[outputPortNumber][readIndex]; out_port[outputPortNumber].packet.address = packet.address; out_port[outputPortNumber].packet.payload = packet.payload; out_port[outputPortNumber].flag = 1; outputBufferReadIndex[outputPortNumber] = (readIndex + 1) % OUTPUT_BUFFER_SIZE; } port_unlock(&(out_port[outputPortNumber])); } } }
void scheduleWriter() { logger_host << INFO << "begin write loop" << std::endl; // terminate if not active while(is_active) { logger_host << FINE << "locking writer ..."; std::unique_lock<std::mutex> lock(writer_mutex); logger_host << " locked" << std::endl; // gpi values are not acknowledged. They are not queued on the board, since there // is virtually now processing time. The value is simply written into memory. for(unsigned char i = 0; i < GPO_COUNT; i++) { // atomically get the old value and set -1 as new value int val = gpos[i]->state.exchange(-1); // skip, if the gpio value was invalid if (val == -1) continue; // append a header with the specified protocol std::vector<int> vals = proto->encode_gpio(i, val); // send the values try { intrfc->send(vals); } catch (mediumException &e) { } catch (protocolException &e) { } } // send all data from in-going ports for(unsigned char i = 0; i < IN_PORT_COUNT; i++) { logger_host << FINE << " trying to lock port " << i << " ..."; // try to lock the port std::unique_lock<std::mutex> port_lock(inPorts[i]->port_mutex, std::try_to_lock); if(port_lock.owns_lock()) logger_host << " success" << std::endl; else logger_host << " failed" << std::endl; // if we could not acquire the lock, continue with the next port if(! port_lock.owns_lock()) continue; // skip the port, if there are values in transit if(*inPorts[i]->transit != 0) continue; // skip the port, if it's task queue is empty if(inPorts[i]->writeTaskQueue->empty()) continue; // TODO this should be set individually for each port binding! not global unsigned int size = QUEUE_SIZE_SW; unsigned int sendSize = std::min(size, proto->max_size()); // gather i values to be sent, where i the minimum of the maximal numbers of values // receivable by the board and the maximal size of a message with the used protocol version std::vector<int> val = take(inPorts[i]->writeTaskQueue, sendSize); // set the transit counter and write variable *inPorts[i]->transit = val.size(); // append a header with the specified protocol val = proto->encode_data(i, val); // send the values try { intrfc->send(val); } catch (mediumException &e) { while(!inPorts[i]->writeTaskQueue->empty()) { std::shared_ptr<abstractWriteState> s = inPorts[i]->writeTaskQueue->take(); s->fail = true; s->m = std::string("could not write values to medium: ") + e.what(); } } catch (protocolException &e) { while(!inPorts[i]->writeTaskQueue->empty()) { std::shared_ptr<abstractWriteState> s = inPorts[i]->writeTaskQueue->take(); s->fail = true; s->m = std::string("protocol encoder reported an exception: ") + e.what(); } } } // sleep, until there is data to write // wake on: // - client-side write (which CAN be sent directly, // i.e. nothing in transit and a previously empty queue) // - server-side ack or poll (received by reader thread) // - shutdown logger_host << FINE << "writer will wait now ..." << std::endl; can_write.wait(lock); } logger_host << INFO << "stopped write loop" << std::endl; }