Microsoft Interview Question
Software Engineer / DevelopersCountry: United States
Interview Type: In-Person
This will result in a deadlock:-
1.) Assume Reader R1 and Writer W1 are blocked on while(wcount) spinlock while W0 is writing data in the structure. (I am assuming that you have a typo in the function init_read, where you actually meant to write "while(wcount)")
2.) As soon as W0 leaves, either W1 or R1 will get chance to exit the spin lock. Assume that W1 gets out first.
3.) Now, W1 will get blocked on while(rcount) since R1 has already taken this rcount++ when it entered the read
4.) R1 can never leave and decrement rcount since W1 is holding the writer's lock.
A possible solution I can think of is- modify init_read() and exchange the order of while(wcount_ and rcount++. Also make sure that they are executed atomically as a group.
As always, feel free to comment and point out any issues or mistakes I have done. Thanks in advance!
Class ReadWriteLock
{
private int Read;
private int Write;
private static Object _S= new Object();
public void ReadWriteLock()
{
Write=0;
Read=0;
}
public void GetReadLock()
{
while(Write);
Interlocked.Increment(ref Read);
}
public void ReleaseReadLock()
{
Interlocked.Decrement(ref Read);
}
public void GetWriteLock()
{
while(Read);
if(Read==0)
{
lock(_S)
{
if(Read==0)
{
Interlocked.Increment(ref Write);
}
}
}
}
}
public void ReleaseWriteLock()
{
Interlocked.Decrement(ref Write);
}
This can be extended to provide mechanism of upgrade and downgrades also it can be extended to provide timeouts for writelocks to avoid deadlock
Example of threaded read and write.
I have a code where thread pool periodically updates a value and 2 separate threads periodically read the current value.
#include<iostream>
#include <boost/thread/locks.hpp>
#include <boost/asio.hpp>
#include <boost/thread/thread.hpp>
#include <boost/asio/io_service.hpp>
class ThreadManager{
public:
ThreadManager(int size):
_threadCount(size){
_work.reset( new boost::asio::io_service::work(_io_service) );
workerThreads();
}
~ThreadManager()
{
_io_service.stop();
_threads.join_all();
}
template<class A,class B, class C>
void post(A func, B instance,C data);
protected:
int _threadCount;
boost::asio::io_service _io_service;
boost::thread_group _threads;
boost::shared_ptr<boost::asio::io_service::work> _work;
void workerThreads(){
for(long x = 0 ; x < _threadCount ; ++x)
{
_threads.create_thread(boost::bind(&boost::asio::io_service::run,
&_io_service));
}
}
};
template<class A,class B, class C>
void ThreadManager::post(A callback, B instance,C data){
_io_service.post(
boost::bind(callback,
instance, data ));
}
class Data{
public:
Data(int jobSize)
{
_readValue = 0;
_realValue = 0;
int thrdId = 1;
boost::thread th1(&Data::getValue,this,thrdId);
++thrdId;
boost::thread th2(&Data::getValue,this,thrdId);
_thrd = new ThreadManager(jobSize);
}
void getValue(int thrdId)
{
while(1)
{
std::string message;
std::string value, threadId;
char cValue[20] ;
char cThrdId[20] ;
sprintf( cValue, "%d", _readValue ) ;
sprintf( cThrdId, "%d", thrdId ) ;
value.assign( cValue ) ;
threadId.assign( cThrdId ) ;
message = "Thrd Id : " + threadId + " Value read : " + value;
std::cout<<std::endl<<message<<std::endl;
sleep(1);
}
}
void updateValue(int count)
{
for(int x = 0; x < count ; ++x)
{
_thrd->post(&Data::setValue,this,x);
}
}
void setValue(int value)
{
boost::mutex::scoped_lock lock(_mutex);
std::cout<<"Value to updated: "<<value<<std::endl;
_realValue = value;
_readValue = _realValue;
}
private:
boost::mutex _mutex;
ThreadManager* _thrd;
int _readValue;
int _realValue;
};
int main()
{
Data inst(5);
while(1)
{
inst.updateValue(3);
sleep(5);
}
return 0;
}
build command: g++ -g -o readWrite readWrite.cpp -I/usr/local/include -lboost_thread -L/usr/local/lib
That's just some fast code from my mind, but I am not sure that's correct. Test that thing is really difficult.
template<typename x_t>
class safe_t {
x_t x; //data for protect
std::atomic<int> q_readers;
std::atomic<int> q_writers;
std::atomic<int> writers_count;
std::atomic<int> writer;
public:
safe_t() : x(), q_readers(0), q_writers(0), writers_count(0), writer(1) {}
x_t read() {
++q_readers;
while(writers_count) { }
x_t tmp(x);
--q_readers;
return x;
}
void write(x_t const &val) {
bool work = true;
int w = ++q_writers;
while(writer != w) { }
while(work) {
++writers_count;
if (q_readers == 0) { // theres is no more readers!
x = val;
work = false;
}
--writers_count;
}
++writer; // wake up next writer!
}
};
class threadSafe
{
private int data;
private int wCount;
private int rCount;
public:
threadSafe(){wCount=0;rCount=0;}
int get()
{
int val;
rCount++;
while(wCount>0);
val=data;
rCount--;
return val;
}
int put(int val)
{
wCount++;
while(wCount!=1);
while(rCount>0);
data=val;
wCount--;
return 1;//success
}
}
This question is basically asking to implement a read-write lock.
- trent.tong September 14, 2013class rwRam
{
private:
unsigned rcount;
unsigned wcount;
void *opaque; // the payload/data.
public:
rwRam(void *o): rcount(0), wcount(0), opaque(o) {}
void init_read();
void fini_read();
void init_write();
void fini_write();
} rwRam;
void rwRam::init_read()
{
/// indicate there is read pending.
rcount ++;
/// wait till there is no writer.
while(!wcount);
}
void rwRam::fini_read()
{
rcount --;
}
void rwRam::init_write()
{
/// need to make sure no reader and no writer.
while(wcount); wcount ++; /// this needs to be a atomic, i.e. done with cmp_xchg on x86.
/// wait for all readers to finish.
while(rcount);
}
void rwRam::fini_write()
{
wcount --;
}