Showing posts with label Technical. Show all posts
Showing posts with label Technical. Show all posts

Wednesday, April 1, 2009

Serialize class member function invocations in C++ with Boost.Thread

Here's the scenario, you have a class that wraps the communication through one USB port, with each member sending one application-specific command(e.g. start/stop device, turn on LED etc ). Usually, the invocations of these member functions need to be mutual exclusive due to the fact that the USB port can only work on one thing at one time. This is not a problem in a single-threaded environment, but a real challenge in a multi-threaded word: what if you have a always-running thread of one member function being dedicated to polling the device, while you need to send other commands to the device on demand?

As this example shows, they will very likely step on each other's feet.



#include <boost/thread/thread.hpp>
#include <boost/thread/mutex.hpp>
#include <iostream>

using std::cout;

class USB
{
public:

void poll()
{
cout << "poll@thread="
<< boost::this_thread::get_id()
<< std::endl;
}
void start()
{
cout << "start@thread="
<< boost::this_thread::get_id()
<< std::endl;
}

void stop()
{
cout << "stop@thread="
<< boost::this_thread::get_id()
<< std::endl;
}

void pause()
{
cout << "pause@thread="
<< boost::this_thread::get_id()
<< std::endl;
}
};

int main(int argc, char* argv[])
{
USB usb;
boost::thread_group threads;

threads.add_thread(new boost::thread(&USB::start, &usb));
threads.add_thread(new boost::thread(&USB::poll, &usb));
threads.add_thread(new boost::thread(&USB::pause, &usb));
threads.add_thread(new boost::thread(&USB::stop, &usb));

threads.join_all();
}

// outputs:

//D:\>usb
//start@thread=poll@thread=pause@thread=stop@thread=00143318
//001432E8001432B800143288
//
//
//
//
//D:\>




The solution is quite simple: add a mutex as a class member so that you can make any two functions mutual exclusive:


#include <boost/thread/thread.hpp>
#include <boost/thread/mutex.hpp>
#include <iostream>

using std::cout;

class USB
{
public:

void poll()
{
boost::mutex::scoped_lock l(m_mutex);

cout << "poll@thread="
<< boost::this_thread::get_id()
<< std::endl;
}
void start()
{
boost::mutex::scoped_lock l(m_mutex);

cout << "start@thread="
<< boost::this_thread::get_id()
<< std::endl;
}

void stop()
{
boost::mutex::scoped_lock l(m_mutex);

cout << "stop@thread="
<< boost::this_thread::get_id()
<< std::endl;
}

void pause()
{
boost::mutex::scoped_lock l(m_mutex);

cout << "pause@thread="
<< boost::this_thread::get_id()
<< std::endl;
}
private:
boost::mutex m_mutex;
};

int main(int argc, char* argv[])
{
USB usb;
boost::thread_group threads;

threads.add_thread(new boost::thread(&USB::start, &usb));
threads.add_thread(new boost::thread(&USB::poll, &usb));
threads.add_thread(new boost::thread(&USB::pause, &usb));
threads.add_thread(new boost::thread(&USB::stop, &usb));

threads.join_all();
}

// outputs:

//D:\>usb
//start@thread=00143288
//poll@thread=001432B8
//pause@thread=001432E8
//stop@thread=00143318
//
//D:\>


It's also possible to only make just two functions mutual exclusive:

#include <boost/thread/thread.hpp>
#include <boost/thread/mutex.hpp>
#include <iostream>

using std::cout;

class USB
{
public:

void poll()
{
//boost::mutex::scoped_lock l(m_mutex);

cout << "poll@thread="
<< boost::this_thread::get_id()
<< std::endl;
}
void start()
{
boost::mutex::scoped_lock l(m_mutex);

cout << "start@thread="
<< boost::this_thread::get_id()
<< std::endl;
}

void stop()
{
boost::mutex::scoped_lock l(m_mutex);

cout << "stop@thread="
<< boost::this_thread::get_id()
<< std::endl;
}

void pause()
{
//boost::mutex::scoped_lock l(m_mutex);

cout << "pause@thread="
<< boost::this_thread::get_id()
<< std::endl;
}
private:
boost::mutex m_mutex;
};

int main(int argc, char* argv[])
{
USB usb;
boost::thread_group threads;

threads.add_thread(new boost::thread(&USB::start, &usb));
threads.add_thread(new boost::thread(&USB::poll, &usb));
threads.add_thread(new boost::thread(&USB::pause, &usb));
threads.add_thread(new boost::thread(&USB::stop, &usb));

threads.join_all();
}

// outputs:
//D:\>usb
//start@thread=poll@thread=pause@thread=001432E8001432B800143288
//stop@thread=00143318
//
//
//
//D:\>


In the code above, only start() and stop() are mutual exclusive.

Tuesday, March 24, 2009

The Hierarchical Concepts of USB

Although I've built some USB related products, I still get confused with some concepts about USB, esp. that ubiquitous word of "setup". Taking the time of re-writing a STMicroelectronics ST7 Tube driver to WinUSB, I sorted the concepts out in case I am lost again.

USB communications are categorized into 4 kinds of TRANSACTIONS, namely,
- Control Transfers
- Interrupt Transfers
- Isochronous Transfers
- Bulk Transfers

Each TRANSACTION is made up of raw PACKETS:
- a Token Packet (Header defining what it expects to follow),
- an Optional Data Packet, (Containing the payload) and a
- a Status Packet (Used to acknowledge transactions and to provide a means of error correction)


Each Packet is a combination of 5 FIELDS:
- Sync : Synchronising clocks
- PID : Packet ID
- ADDR : Device Address
- ENDP : Endpoit on thhis Device
- CRC
- EOP : End of Packet

And the combination is NOT random, actually only 4 of them:
- Token Packets = Sync + PID + ADDR + ENDP + CRC5 + EOP
- Data Packets = Sync + PID + Data + CRC16 + EOP
- Handshake Packets = Sync + PID + EOP
- Start of Frame Packets = Sync + PID + Frame Number + CRC5 + EOP

Sepcial notes about "SETUP":

There is a kind of PID named SETUP amongst other types such as IN and OUT, only a Token Packets has a SETUP PID.

A Token Packet with the PID being SETUP can only appear in Control Transfers, and only in the SETUP STAGE.

Control Transfers have 3 stages: SETUP, DATA, and STATUS stages. In the latter 2 stages, no Token Packets with PID being SETUP would appear. only Token Packets with PID being IN or OUT, followed by Data Packets are used.

SETUP PACKETS are packets that are sent through Control Transfers. They cover packets in all the 3 stages aforementioned, but not only the Token Packets with PID being SETUP.

SETUP PACKETS are so called because they are used to set up the device including the detection and configuration of the device and carrying out common functions such as setting the USB device’s address, requesting a device descriptor or checking the status of a endpoint. Hence the concept of STANDARD REQUESTS, namely,
STANDARD DEVICE REQUESTS:
- get_status(0x00)
clear_feature(0x01)
set_feature(0x03)
set_address(0x05)
get_descriptor(0x06)
set_descriptor(0x07)
get_configuration(0x08)
set_configuration(0x09)

- STANDARD INTERFACE REQUESTS:
get_status(0x00)
clear_feature(0x01)
set_feature(0x03)
get_interface(0x0a)
set_interface(0x11)

- STANDARD ENDPOINT REQUESTS:
get_status(0x00)
clear_feature(0x01)
set_feature(0x03)
synch_frame(0x12)


While writting software on the Host(e.g. PC), SETUP PACKETS are wrapped into data structs such as WINUSB_SETUP_PACKET in WinUSB, which correspond to the whole 3-stage Control Transfers, not the SETUP STAGE, neither the token packets with PID being SETUP.

this USB-in-a-nutshell is the best USB tutorial online and where the above info are extracted from.

Get Current Time in C++

The ATL/MFC way:
/*Output Current Time is: 03/24/09 15:54:43*/



#include <atltime.h> // for CTime

#include <atlstr.h> // for CString

#include <iostream>



int main()

{

CString s(CTime::GetCurrentTime().Format("%c"));

std::wcout << L"Current Time is: "<< s.GetString();

}







The Boost Way:

/* Uses the clock to get the local time

*

* Expected Output something like:

* 2002-Mar-08 16:30:59

*/



#include "boost/date_time/posix_time/posix_time.hpp"

#include <iostream>



int main()

{

using namespace boost::posix_time;

using namespace boost::gregorian;



//get the current time from the clock -- one second resolution

ptime now = second_clock::local_time();



std::cout << L"Current Time is: "

<< to_simple_wstring(now)

<< std::endl;

return 0;

}