XLL+ Class Library

AvgOptData class

The first step in converting the project from synchronous to asynchronous operation is to implement a class which contains all the inputs and outputs of a single calculation. It will be used in several ways:

  1. The add-in function creates it and populates it with all the input values passed by Excel.
  2. It is passed to a worker thread, which uses all its inputs for the calculation.
  3. The worker thread populates the outputs when the calculation is complete.
  4. It is placed in the cache, where it can be compared with the inputs of the add-in function in future calls.

Data members

The class contains three sets of data:

  1. Input values, as passed from Excel.
  2. Output values, as calculated by the worker thread.
  3. A state flag, which indicates whether the output values are valid.

The signature of the existing calculation function is:

extern "C"
int eval_avg_opt
(
    int     put,
    double  spot,
    double  sigma,
    double  loan,
    double  discount,
    double  div_yield,
    long    value_date,
    long    maturity,
    long    avg_in_count,
    long    *avg_in_dates,
    long    avg_out_count,
    long    *avg_out_dates,
    long    iters,
    double* result,
    char*   err_buf,
    int     err_buf_size
);

The data members of the AvgOptData class will therefore be as follows:

class AvgOptData
{
    // Data members
public:
    // Inputs
    int                 put;
    double              spot;
    double              sigma;
    double              loan;
    double              discount;
    double              div_yield;
    long                value_date;
    long                maturity;
    std::vector<long>   avg_in_dates;
    std::vector<long>   avg_out_dates;
    long                iters;

    // Outputs
    double              result;
    char                err_buf[512];
    int                 rc;
    bool                done;
    ...
};

Methods

The class contains three functions and an overloaded operator:

class AvgOptData
{
    ...
    // Constructor
    AvgOptData(int put_, double spot_, double sigma_, double loan_, 
        double discount_, double div_yield_, long value_date_, 
	    long maturity_, const std::vector<long>& avg_in_dates_, 
	    const std::vector<long>& avg_out_dates_, long iters_) 
    {
        // Initialize output
        ... 
        // Copy inputs
        ... 
    }

    // Evaluation
    void Evaluate()
    {
        rc = eval_avg_opt(put ? 1 : 0, spot, sigma, loan, discount, 
            div_yield, value_date, maturity, avg_in_dates.size(), 
            avg_in_dates.size() ? &avg_in_dates[0] : 0, 
            avg_out_dates.size(), 
            avg_out_dates.size() ? &avg_out_dates[0] : 0, 
            iters, &result, err_buf, sizeof(err_buf));
        done = true;
    }

    // Comparison
    static int Compare(const AvgOptData& x, const AvgOptData& y)
    {
        // Compare only inputs
        ...
    }
    bool operator<(const AvgOptData& c) const 
    { 
        return Compare(*this, c) < 0; 
    }
};

The methods are used as follows:

  1. The constructor is called by the add-in function, to populate the object with the inputs passed from Excel.
  2. The evaluation function is called by a worker thread.
  3. The comparison function and operator are used by the cache, to look up an AvgOptData object based on the value of its inputs.

Multithreaded access

The AvgOptData object is used by the main thread and by a worker thread. It is therefore important to make sure that there are no thread conflicts when writing and reading the object.

There are 4 points at which the object is accessed:

Creation
This includes: construction, population of inputs and insertion into the cache. The main thread is responsible for this. The object is then passed to the ThreadManager.
Calculation
The worker thread is responsible for calculation. This includes: reading the inputs, passing them to the calculation function eval_avg_opt(), populating the results and finally, setting the done flag.
Reading results
The main thread may at any time after construction attempt to read the results. First, the done flag is inspected. If it is false, then no further reading takes place. If it is true, then we know that the worker thread has finished using the object and that the outputs are fully populated. The main thread can therefore safely read the outputs and pass them back to Excel.
Closedown
During closedown, the main thread deletes all the AvgOptData objects from the cache. However it does this only after the ThreadManager has terminated all the worker threads; thus contention is not a problem.

Next: AvgOptDataCache class >>