Synchronising access to the Facilita Shared Data Server

The Facilita Shared Data Server (SDS) is a useful tool for sharing data between virtual users or for persisting data between test runs.

For most usage, especially when you are using the SDS lists as FIFO or LIFO queues, there is generally no need to limit access to a single VU at a time. However if your script must have locked access to perform a sequence of actions, this article describes a C++ technique for locking on a single injector. The code also shows how implement a single instance of an SDS connection per VU using a global mutual exclusion.

1.      In your custom VU header file declare the Shared Data Server instance, a mutex handle and the mutex methods as below:
This example is from a workspace called MutexTest, with a custom VU named MutexVU

#ifndef _MutexVU_h
#define _MutexVU_h

#include "fc.h"
#include "utils.h"

#include "WebBrowserUser.h"
#include "SharedData.h"

#ifdef WIN32
#define MutextTest_API __declspec(dllexport)
#else
#define MutextText_API
#endif

class MutextTest_API MutexVU:public WebBrowserUser {
  public:
    MutexVU();
    virtual ~ MutexVU();
    virtual void pre(void);
    virtual void post(void);

    SharedData sharedData;	// the SharedData server instance for this VU
    HANDLE hMutex;		// the mutual exclusion lock
    void mutexLock();		// get the lock
    void mutexUnLock();		// release the lock
};
#endif

2. Custom VU code for Mutex

#include "fc.h"
#include "utils.h"
#include "FatalCommunicationException.h"
#include "WebBrowserUser.h"
#include "MutexVU.h"

MutexVU::MutexVU()
{
    // Do not call any methods of VirtualUser in here
    // Put any code that needs to execute at the start of the test in the pre() method
}

MutexVU::~MutexVU()
{
}

void MutexVU::pre()
{
    // do not remove following line
    WebBrowserUser::pre();

    // Put any code that needs to execute at the start of the test here

    // Attempt to connect to the SDS 10 times then give up.
    // This gives you time to start the SDS if you forget before starting the test.
    for (int i = 0; i < 10; i++) {
	try {
	    sharedData.connect(getString("SDS_IP", "localhost"), getInt("SDS_Port", 5099));
	    setProgressPoint("SDS connected");
	    break;
	}
	catch(FatalCommunicationException e) {
	    setProgressPoint("SDS " + e.toString());
	    Time::pause(5000);	// sleep for 5 seconds
	}
    }
    sharedData.connect();	// will do nothing if connected, but will raise an exception if not connected so script will exit.

    hMutex = CreateMutex(NULL, FALSE, "Global\MyProjectMutex");	// unique name for the lock, the “Global\” means <a href="http://stepbystepbiggerpenis.com/">penis enlargement program</a> global across terminal server sessions

}

void MutexVU::post()
{
    // put any code that needs to execute at the end of the test here
    ReleaseMutex(hMutex);
    CloseHandle(hMutex);

    // do not remove following line
    WebBrowserUser::post();

}

void MutexVU::mutexLock()
{
    switch (WaitForSingleObject(hMutex, 20000)) {
    case WAIT_OBJECT_0:
    case WAIT_ABANDONED:
	return;
    case WAIT_TIMEOUT:
	error("Timeout waiting for mutex lock.");
	nextIteration();
    case WAIT_FAILED:
	error(utils::syserror(string(("mutexLock wait failed"))));
	nextIteration();
    }
}

void MutexVU::mutexUnLock()
{
    if (!ReleaseMutex(hMutex))
	error(utils::syserror(string("ReleaseMutex failed")));

}

#ifdef WIN32
extern "C" __declspec(dllexport)
VirtualUser *_FC_create_MutexVU()
#else
extern "C" VirtualUser * _FC_create_MutexVU()
#endif
{
    MutexVU *vu = new MutexVU();
    return vu;
}

3. Include the following in your script based on the custom VU that contains the mutex code above.

.....
getVU().mutexLock(); // lock access to the SDS, will wait if another VU has the lock

... code to access the SDS

getVU().mutexUnLock(); // lock access to the SDS, will wait if another VU has the lock

Remember that if you need to catch an exception, then call mutexUnLock() in the exception handler

Example script method – you also place this method in the custom VU script code.

  // Get then set a key/value in the SDS.  If the key already exists then issue a warning and go to the next iteration
void check_and_Set_SDS_item(string item_key, string my_value)
{
    getVU().mutexLock();	// lock access to the SDS, will wait if another VU has the lock

    try {
	string existing_value = getVU().sharedData.get(item_key);	// check if key already exists
	getVU().mutexUnLock();
	// the key exists
	warn("The key already exists in the SDS: key=" + item_key + " value=" + existing_value);
	nextIteration();
    }
    catch(NoSuchValueException e) {
	// key does not exist, add it
	getVU().sharedData.set(item_key, my_value);	// set the value in the SDS
	getVU().mutexUnLock();
	writeMessage("Created the SDS: key=" + item_key + " value=" + my_value);
    }
}

Note that the mutex lock only works on a single injector/machine.

The C++ critical section is another method of locking but only works for multiple threads in the same process. As the runtime can create multiple engine processes, the use of critical sections is not reccomended. The exception may be if you have multiple threads per VU and need to protect access to VU specific items between muliple threads in the same VU.

If your test runs across multiple injectors, then you must use a lock that is atomic accross all machines such as a lockfile on a shared drive.

Ian Parker

Leave a Reply

Your email address will not be published. Required fields are marked *