I am using cpuset from the command line (i.e. http://man7.org/linux/man-pages/man7/cpuset.7.html) to run a C/C++ program.
I wonder whether the C/C++ is able to retrieve the cpuset it is running on top of.
I read http://man7.org/linux/man-pages/man3/CPU_SET.3.html but I don't see any macro there to be able to achieve what I want.
The main reason I would like to retrieve the cpuset inside the program is to fill cpu_set_t* so that I can pass it to pthread_attr_setaffinity_np().
Thanks in advance.
cpu_set_t cpuset;
CPU_ZERO(&cpuset);
if (0 == sched_getaffinity(getpid(), sizeof(cpu_set_t), &cpuset)) {
const long nCores = sysconf( _SC_NPROCESSORS_ONLN );
for (long i = 0; i < nCores; i++) {
if (CPU_ISSET(i, &cpuset)) {
std::cout << "core # " << i << " is in cpuset" << std::endl;
}
}
}
else {
std::cerr << "sched_getaffinity() failed: " << strerror(errno) << std::endl;
}
U can work on CPU set as
#define _GNU_SOURCE
#include <sched.h>
cpu_set_t my_set;
CPU_ZERO(&my_set);
CPU_SET(1, &my_set); // here 1 is the cpu 1 similarly u can set more
CPU_SET(2, &my_set); // here 2 is the cpu 2 similarly u can set more
pthread_setaffinity_np(pthread_self(), sizeof (cpu_set_t), &my_set);
Related
I'm playing around with io_uring, https://kernel.dk/io_uring.pdf, to see if it can be used for async file I/O for logging. This is a simple program that opens a file, stats the file, and then reads the first 4k from the file. This program runs to completion successfully when the file exists and is readable. But the user_data field in the completion queue entry is always zero. The documentation for io_uring says:
user_data is common across op-codes, and is untouched by the kernel. It's simply copied to the completion event, cqe, when a completion event is posted for this request.
Since the completions are not ordered the user_data field is needed to match completions with submissions. If the field is always zero then how can it be used?
#include <iostream>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <liburing.h>
#include <stdlib.h>
int main() {
struct io_uring ring;
// see man io_uring_setup for what this does
auto ret = io_uring_queue_init(64, &ring, 0);
if (ret) {
perror("Failed initialize uring.");
exit(1);
}
std::cout << "I/O uring initialized successfully. " << std::endl;
auto directory_fd = open("/tmp", O_RDONLY);
if (directory_fd < 0) {
perror("Failed to open current directory.");
exit(1);
}
struct io_uring_sqe *submission_queue_entry = io_uring_get_sqe(&ring);
submission_queue_entry->user_data = 100;
io_uring_prep_openat(submission_queue_entry, directory_fd, "stuff", O_RDONLY, 0);
submission_queue_entry = io_uring_get_sqe(&ring);
submission_queue_entry->user_data = 1000;
struct statx statx_info;
io_uring_prep_statx(submission_queue_entry, directory_fd, "stuff", 0, STATX_SIZE, &statx_info);
//TODO: what does this actually return?
auto submit_error = io_uring_submit(&ring);
if (submit_error != 2) {
std::cerr << strerror(submit_error) << std::endl;
exit(2);
}
int file_fd = -1;
uint32_t responses = 0;
while (responses != 2) {
struct io_uring_cqe *completion_queue_entry = 0;
auto wait_return = io_uring_wait_cqe(&ring, &completion_queue_entry);
if (wait_return) {
std::cerr << "Completion queue wait error. " << std::endl;
exit(2);
}
std::cout << "user data " << completion_queue_entry->user_data << " entry ptr " << completion_queue_entry << " ret " << completion_queue_entry->res << std::endl;
std::cout << "size " << statx_info.stx_size << std::endl;
io_uring_cqe_seen(&ring, completion_queue_entry);
if (completion_queue_entry->res > 0) {
file_fd = completion_queue_entry->res;
}
responses++;
}
submission_queue_entry = io_uring_get_sqe(&ring);
submission_queue_entry->user_data = 66666;
char buf[1024 * 4];
io_uring_prep_read(submission_queue_entry, file_fd, buf, 1024 * 4, 0);
io_uring_submit(&ring);
struct io_uring_cqe* read_entry = 0;
auto read_wait_rv = io_uring_wait_cqe(&ring, &read_entry);
if (read_wait_rv) {
std::cerr << "Error waiting for read to complete." << std::endl;
exit(2);
}
std::cout << "Read user data " << read_entry->user_data << " completed with " << read_entry->res << std::endl;
if (read_entry->res < 0) {
std::cout << "Read error " << strerror(-read_entry->res) << std::endl;
}
}
Output
I/O uring initialized successfully.
user data 0 entry ptr 0x7f4e3158c140 ret 5
size 1048576
user data 0 entry ptr 0x7f4e3158c150 ret 0
size 1048576
Read user data 0 completed with 4096
What happens if you try and set user_data after your calls to io_uring_prep_openat()/io_uring_prep_statx()?
I ask this because doing a Google search for io_uring_prep_statx suggests it comes from liburing library.
Searching the liburing source for io_uring_prep_openat leads us to a definition of io_uring_prep_openat() in liburing.h:
static inline void io_uring_prep_openat(struct io_uring_sqe *sqe, int dfd,
const char *path, int flags, mode_t mode)
{
io_uring_prep_rw(IORING_OP_OPENAT, sqe, dfd, path, mode, 0);
sqe->open_flags = flags;
}
Searching the liburing source for io_uring_prep_statx leads to a definition of io_uring_prep_statx():
static inline void io_uring_prep_statx(struct io_uring_sqe *sqe, int dfd,
const char *path, int flags, unsigned mask,
struct statx *statxbuf)
{
io_uring_prep_rw(IORING_OP_STATX, sqe, dfd, path, mask,
(__u64) (unsigned long) statxbuf);
sqe->statx_flags = flags;
}
Chasing the calls gets us to the definition of io_uring_prep_rw:
static inline void io_uring_prep_rw(int op, struct io_uring_sqe *sqe, int fd,
const void *addr, unsigned len,
__u64 offset)
{
sqe->opcode = op;
sqe->flags = 0;
sqe->ioprio = 0;
sqe->fd = fd;
sqe->off = offset;
sqe->addr = (unsigned long) addr;
sqe->len = len;
sqe->rw_flags = 0;
sqe->user_data = 0;
sqe->__pad2[0] = sqe->__pad2[1] = sqe->__pad2[2] = 0;
}
PS: I notice you have a comment that says
//TODO: what does this actually return?
auto submit_error = io_uring_submit(&ring);
Well, if we search the liburing repo for "int io_uring_submit" we come across the following in src/queue.c:
/*
* Submit sqes acquired from io_uring_get_sqe() to the kernel.
*
* Returns number of sqes submitted
*/
int io_uring_submit(struct io_uring *ring)
This ultimately chains calls down to io_uring_enter() syscall (raw man page) so you can read that for more detail.
Update: The questioner says moving the assignment solved their problem so I invested some time thinking about the text they quoted. Upon further reading I have picked up a subtlety (emphasis added):
user_data is common across op-codes, and is untouched by the kernel. It's simply copied to the completion event, cqe, when a completion event is posted for this request.
There's a similar statement earlier in the document (again emphasis added):
The cqe contains a user_data field. This field is carried from the
initial request submission, and can contain any information that the the application needs to identify said request. One common use case is to have it be the pointer of the original request. The kernel will not touch this field, it's simply carried straight from submission to completion event.
The statement applies to io_uring kernel syscalls but io_uring_prep_openat() / io_uring_prep_statx() are liburing functions. liburing is a userspace helper library so the statements above about user_data do not have to apply to all liburing functions.
If the field is always zero then how can it be used?
The field is being zeroed by certain liburing preparation helper functions. In this case it can be only be set (and retain the new value) after those helper function have been called. The io_uring kernel syscalls behave per the quote.
I'm trying to get the usb device serial number from a C code application in a embedded Linux platform (petalinux) using libusb.
I've seen a few similar questions which helped me to get some direction but unfortunately I didn't manage to solve it yet.
First I'll show what I'm trying to read, this is the dmesg of the device when is plugged in:
[ 9.632965] usb 1-1.4: New USB device found, idVendor=0403, idProduct=de37
[ 9.639837] usb 1-1.4: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[ 9.647146] usb 1-1.4: Product: PI CAM
[ 9.651153] usb 1-1.4: Manufacturer: MANUF
[ 9.655326] usb 1-1.4: SerialNumber: 20102130
[ 9.688519] uvcvideo: Found UVC 1.00 device PI IMAGER (0403:de37)
[ 9.695084] uvcvideo 1-1.4:1.0: Entity type for entity Processing 3 was not initialized!
[ 9.703177] uvcvideo 1-1.4:1.0: Entity type for entity Camera 1 was not initialized!
[ 9.711098] input: PI CAM: PI CAM as /devices/platform/amba/ff9d0000.usb0/fe200000.dwc3/xhci-hcd.0.auto/usb1/1-1/1-1.4/1-1.4:1.0/input/input0
What I'm trying to read is the serial number: 20102130
I've tried the next code:
libusb_device*** list=NULL;
libusb_context *context = NULL;
libusb_init(&context);
unsigned char serial[200];
int status = libusb_get_string_descriptor_ascii(device,desc.iSerialNumber, serial,200);
if(status < 0)
{
printf("Index: %d Error: %d\n",desc.iSerialNumber, status);
}else{
printf("Themal camera serial: %s\n",serial);
}
But it gives a -1 error, it prints: "Index: 3 Error: -1" . If I remove the error checking I've got the serial: bA6` which it doesn't make any sense...
Does anyone know how could I retrieve/read that serial number from my application?
Thanks in advance.
#include <iostream>
#include <libusb-1.0/libusb.h>
#include <cstdio>
#include <unordered_map>
using namespace std;
int main() {
libusb_device **devs;
libusb_context *ctx = nullptr; // libusb session context
int r; // for results
ssize_t cnt; // number of usb devices found
r = libusb_init(&ctx); // open session
if (r < 0) {
cerr << "Error: initialization failed: " << r << endl;
return 1;
}
// set the verbosity level of debug messages
libusb_set_debug(ctx, 3);
// get a list of all found USB devices
cnt = libusb_get_device_list(ctx, &devs);
if (cnt < 0) {
cerr << "Error: USB device list not received." << endl;
return 1;
}
libusb_device_descriptor desc{};
for (int i = 0; i < cnt; i++) {
libusb_device_handle *handle;
auto *data = new uint8_t[33]();
r = libusb_get_device_descriptor(devs[i], &desc);
if (r < 0) {
cerr << "Error: Device handle not received, code: " << r << endl;
}
printf("%02X:%02X \t", desc.idVendor, desc.idProduct);
try {
libusb_open(devs[i], &handle);
if (handle != nullptr) {
if (libusb_get_string_descriptor_ascii(handle, desc.iSerialNumber, data, 31) >= 0) {
data[32] = '\0';
cout << "Serial Number: \"" << data << "\"";
}
}
libusb_close(handle);
} catch (libusb_error &e) {
cerr << e << endl;
}
cout << endl;
}
// release the memory allocated by the get device list function
libusb_free_device_list(devs, 1);
libusb_exit(ctx); // close session,
return 0;
}
// https://www.keil.com/pack/doc/mw/USB/html/_u_s_b__interface__descriptor.html
// https://libusb.sourceforge.io/api-1.0
Gives me an output:
...
1D6B:02 Serial Number: "0000:05:00.3"
...
I'm trying to port a program from Windows to Linux.
I encountered a problem when I found out that there isn't a "real" ReadProcessMemory counterpart on Linux; I searched for an alternative and I found ptrace, a powerful process debugger.
I quickly coded two small console applications in C++ to test ptrace, before using it in the program.
TestApp
This is the tracee; it keeps printing two integers every 50 milliseconds while increasing their value by 1 every time.
#include <QCoreApplication>
#include <QThread>
#include <iostream>
using namespace std;
class Sleeper : public QThread
{
public:
static void usleep(unsigned long usecs){QThread::usleep(usecs);}
static void msleep(unsigned long msecs){QThread::msleep(msecs);}
static void sleep(unsigned long secs){QThread::sleep(secs);}
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
int value = 145;
int i = 0;
do {
cout << "i: " << i << " " << "Value: " << value << endl;
value++;
i++;
Sleeper::msleep(50);
} while (true);
return a.exec();
}
MemoryTest
This is the tracer; it asks for the process name and retrieves the PID using the command pidof -s, then ptrace attaches to the process and retrieves the memory address' value every 500 milliseconds, for 10 times.
#include <QCoreApplication>
#include <QThread>
#include <iostream>
#include <string>
#include <sys/ptrace.h>
#include <errno.h>
using namespace std;
class Sleeper : public QThread
{
public:
static void usleep(unsigned long usecs){QThread::usleep(usecs);}
static void msleep(unsigned long msecs){QThread::msleep(msecs);}
static void sleep(unsigned long secs){QThread::sleep(secs);}
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
char process_name[50];
cout << "Process name: ";
cin >> process_name;
char command[sizeof(process_name) + sizeof("pidof -s ")];
snprintf(command, sizeof(command), "pidof -s %s", process_name);
FILE* shell = popen(command, "r");
char pidI[sizeof(shell)];
fgets(pidI, sizeof(pidI), shell);
pclose(shell);
pid_t pid = atoi(pidI);
cout << "The PID is " << pid << endl;
long status = ptrace(PTRACE_ATTACH, pid, NULL, NULL);
cout << "Status: " << status << endl;
cout << "Error: " << errno << endl;
unsigned long addr = 0x012345; // Example address, not the true one
int i = 0;
do {
status = ptrace(PTRACE_PEEKDATA, pid, addr, NULL);
cout << "Status: " << status << endl;
cout << "Error: " << errno << endl;
i++;
Sleeper::msleep(500);
} while (i < 10);
status = ptrace(PTRACE_DETACH, pid, NULL, NULL);
cout << "Status: " << status << endl;
cout << "Error: " << errno << endl;
return a.exec();
}
Everything works fine, but TestApp is paused (SIGSTOP) until ptrace detaches from it.
Also, when it attaches to the process, the status is 0 and the error is 2; the first time it tries to retrieve the memory address value it fails with status -1 and error 3. Is it normal?
Is there a way to prevent ptrace from sending the SIGSTOP signal to the process?
I already tried using PTRACE_SEIZE instead of PTRACE_ATTACH, but it doesn't work: status -1 and error 3.
Update: Using Sleeper in MemoryTest before the "do-while" loop fixes the problem of the first memory address value retrieval, even if the value of seconds, milliseconds or microseconds is 0. Why?
After a lot of research I'm pretty sure that there isn't a way to use ptrace without stopping the process.
I found a real ReadProcessMemory counterpart, called process_vm_readv, which is much more simple.
I'm posting the code in the hope of helping someone who is in my (previous) situation.
Many thanks to mkrautz for his help coding MemoryTest with this beautiful function.
#include <QCoreApplication>
#include <QThread>
#include <sys/uio.h>
#include <stdint.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <iostream>
using namespace std;
class Sleeper : public QThread
{
public:
static void usleep(unsigned long usecs){QThread::usleep(usecs);}
static void msleep(unsigned long msecs){QThread::msleep(msecs);}
static void sleep(unsigned long secs){QThread::sleep(secs);}
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
char process_name[50];
cout << "Process name: ";
cin >> process_name;
char command[sizeof(process_name) + sizeof("pidof -s ")];
snprintf(command, sizeof(command), "pidof -s %s", process_name);
FILE* shell = popen(command, "r");
char pidI[sizeof(shell)];
fgets(pidI, sizeof(pidI), shell);
pclose(shell);
pid_t pid = atoi(pidI);
cout << "The PID is " << pid << endl;
if (pid == 0)
return false;
struct iovec in;
in.iov_base = (void *) 0x012345; // Example address, not the true one
in.iov_len = 4;
uint32_t foo;
struct iovec out;
out.iov_base = &foo;
out.iov_len = sizeof(foo);
do {
ssize_t nread = process_vm_readv(pid, &out, 1, &in, 1, 0);
if (nread == -1) {
fprintf(stderr, "error: %s", strerror(errno));
} else if (nread != in.iov_len) {
fprintf(stderr, "error: short read of %li bytes", (ssize_t)nread);
}
cout << foo << endl;
Sleeper::msleep(500);
} while (true);
return a.exec();
}
Davide,
Have you had a look at the /proc filesystem? It contains memory map files that can be used to peek at the full process space. You can also write in the space to set a breakpoint. There is a wealth of other information in /proc as well.
The PTRACE_CONT command can be used to continue a process. Generally, the target will be paused with a PTRACE_ATTACH when the debugger attaches.
The man page says PTRACE_SIEZE should not pause the process. What flavor and version of Linux are you using? PTRACE_SIEZE has been around for quite awhile so I'm not sure why you are having trouble there.
I note the addr value is set to 0x12345. Is this a valid address in the target space? Or was that just an example? How is the stack address of interest (&value) communicated between the two processes?
I'm not too sure about the return codes. Generally a 0 means all is well, the errno may just be a hangover value from the last error.
--Matt
I want to generate many ec key pairs. Speeding up the process a bit, I rewrote my appication to use multiple threads for this job. Here is a code snippet of the way each thread wants to generate the keys:
(...)
EC_KEY* _ec_key = EC_KEY_new();
EC_GROUP* ec_group_new = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1);
const EC_GROUP* ec_group = ec_group_new;
if (!EC_KEY_set_group(ec_key,ec_group))
DieWithError("Error in initializeCrypto, EC_KEY_set_group failed!");
// Segfault at this position
if(!EC_KEY_generate_key(ec_key))
DieWithError ("Error in generateKeys, EC_KEY_generate_key failed!");
(...)
EC_GROUP_free(ec_group_new);
EC_KEY_free(ec_key);
Ok at the first glance, everything seemed to work fine. The applications ran twice as fast using four threads on my i5 520m. But then after 3-4 E6 key generations it suddenly segfaults. If I lock the EC_KEY_generate_key operation there is no segfault anymore, but the advantage of using multiple threads is gone. Now my questions. Is it possible split the creation of keys into multiple threads without corrupting memory? I didn't found any information using google. The SSL Docu doesn't mention anything about thread-safety, though. Any help is highly appreciated. thx
// Segfault at this position
if(!EC_KEY_generate_key(ec_key))
DieWithError ("Error in generateKeys, EC_KEY_generate_key failed!");
...
... But then after 3-4 E6 key generations it suddenly segfaults.
You are using OpenSSL's random number generator, and its not thread safe. Below is from cryptlib.c around line 125. Notice the random number generators and the elliptic curve gear make the list.
/* real #defines in crypto.h, keep these upto date */
static const char* const lock_names[CRYPTO_NUM_LOCKS] =
{
"<<ERROR>>",
"err",
"ex_data",
"x509",
"x509_info",
"x509_pkey",
"x509_crl",
"x509_req",
...
"ssl_ctx",
"ssl_session",
"ssl",
"ssl_method",
"rand",
"rand2",
...
"ecdsa",
"ec",
"ecdh",
"bn",
"ec_pre_comp",
...
};
You have to explicitly set the locks. See OpenSSL's threads(3).
Is it possible split the creation of keys into multiple threads without corrupting memory?
Yes, but you have to use OpenSSL's locking mechanism.
Here's what my OpenSSL initialization routine looks like in C++. It initializes the locks and sets the callbacks.
pthread_mutex_t s_locks[CRYPTO_NUM_LOCKS] = { };
void Initialize()
{
static once_flag init;
std::call_once(init, []() {
// Standard OpenSSL library init
OPENSSL_no_config();
SSL_library_init();
SSL_load_error_strings();
OpenSSL_add_ssl_algorithms();
// Lock setup
LOCK_setup();
CALLBACK_setup();
});
}
void LOCK_setup()
{
ASSERT(CRYPTO_NUM_LOCKS == CRYPTO_num_locks());
if(CRYPTO_NUM_LOCKS != CRYPTO_num_locks())
throw runtime_error("CRYPTO_NUM_LOCKS mismatch");
for(unsigned i = 0; i < CRYPTO_NUM_LOCKS; ++i)
{
int rc = pthread_mutex_init(&s_locks[i], NULL);
ASSERT(rc == 0);
if(!(rc == 0))
throw runtime_error("pthread_mutex_init");
}
}
void CALLBACK_setup()
{
CRYPTO_set_id_callback(&ThreadIdFnc);
CRYPTO_set_locking_callback(&LockingFnc);
}
void LockingFnc(int mode, int idx, const char* file, int line)
{
ASSERT(mode == CRYPTO_LOCK || mode == CRYPTO_UNLOCK);
ASSERT(CRYPTO_NUM_LOCKS == CRYPTO_num_locks());
ASSERT(idx >= 0 && idx < CRYPTO_NUM_LOCKS);
if(!(idx >= 0 && idx < CRYPTO_NUM_LOCKS))
{
ostringstream oss;
oss << "LockingFnc: lock failed with bad index ";
oss << idx << ". File: " << (file ? file : "Unknown");
oss << ", line: " << line;
// Log oss.str()
return;
}
if((mode & CRYPTO_LOCK) == CRYPTO_LOCK)
{
int rc = pthread_mutex_lock(&s_locks[idx]);
int err = errno;
ASSERT(rc == 0);
if(!(rc == 0))
{
ostringstream oss;
oss << "LockingFnc: lock failed with error ";
oss << err << ". File: " << (file ? file : "Unknown");
oss << ", line: " << line;
throw runtime_error(oss.str());
}
}
else if((mode & CRYPTO_UNLOCK) == CRYPTO_UNLOCK)
{
int rc = pthread_mutex_unlock(&s_locks[idx]);
int err = errno;
ASSERT(rc == 0);
if(!(rc == 0))
{
ostringstream oss;
oss << "LockingFnc: unlock failed with error ";
oss << err << ". File: " << (file ? file : "Unknown");
oss << ", line: " << line;
throw runtime_error(oss.str());
}
}
}
unsigned long ThreadIdFnc()
{
#if defined(AC_OS_APPLE)
ASSERT(sizeof(unsigned long) >= sizeof(pid_t));
return static_cast<unsigned long>(pthread_mach_thread_np(pthread_self()));
#elif defined(AC_OS_STARNIX)
ASSERT(sizeof(unsigned long) >= sizeof(pid_t));
return static_cast<unsigned long>(gettid());
#else
# error "Unsupported platform"
#endif
}
If you are not using libssl, then forgo the call to SSL_library_init. All libcrypto needs is the call to OpenSSL_add_all_algorithms to initialize.
The SSL Documentation doesn't mention anything about thread-safety, though.
Yeah, the docs leave something to be desired at times. I know a bunch of folks are working on improving it through a wiki run by the OpenSSL Foundation. Matt Caswell has done a lot of work in simply documenting the elliptic curve stuff at http://wiki.openssl.org/index.php/Elliptic_Curve_Cryptography. He's also responsible for the POD files and MAN pages. Keep in mind that Matt did not write any of the code - he's just documenting it for others.
There's a page on initialization, but it does not have the code for the locks. Its on my TODO list. See http://wiki.openssl.org/index.php/Library_Initialization.
It could probably be a simple question but I couldn't find a clear answer for it. I have multiple threads in c code and one of them uses select to wait for n seconds. The question that I have is that does it blocks the entire process for n seconds (like usleep) or does select blocks only the calling thread (more like nanosleep).
Thanks for the answers.
I've seen several implementations in which one thread is blocking on select while other threads continue processing - so, yes, it only blocks the running thread.
(Sorry for not bringing any references)
The POSIX spec for select specifically mentions "thread" in only one place, where it talks about restoring the signal mask of the calling thread by pselect().
As with the other answers, my experience also says the answer is yes, it only blocks the calling thread.
Yes. A sloppy but still pretty conclusive test.
#include <iostream>
#include <pthread.h>
#include <sys/time.h>
using namespace std;
pthread_mutex_t cout_mutex = PTHREAD_MUTEX_INITIALIZER;
void *task1(void *X)
{
timeval t = {0, 100000};
for (int i = 0; i < 10; ++i)
{
pthread_mutex_lock(&cout_mutex);
cout << "Thread A going to sleep" << endl;
pthread_mutex_unlock(&cout_mutex);
select(0, NULL, NULL, NULL, &t);
pthread_mutex_lock(&cout_mutex);
cout << "Thread A awake" << endl;
pthread_mutex_unlock(&cout_mutex);
}
return (NULL);
}
void *task2(void *X)
{
pthread_mutex_lock(&cout_mutex);
cout << "Thread B down for the long sleep" << endl;
pthread_mutex_unlock(&cout_mutex);
timeval t = {5, 0};
select(0, NULL, NULL, NULL, &t);
pthread_mutex_lock(&cout_mutex);
cout << "Thread B glad to be awake" << endl;
pthread_mutex_unlock(&cout_mutex);
return (NULL);
}
int main(int argc, char *argv[])
{
pthread_t ThreadA,ThreadB;
pthread_create(&ThreadA,NULL,task1,NULL);
pthread_create(&ThreadB,NULL,task2,NULL);
pthread_join(ThreadA,NULL);
pthread_join(ThreadB,NULL);
return (0);
}