Errors in Cilk

In Cilk, error handling is typically done using exceptions, which is different from the explicit error return values used in some other languages. Here’s how we can implement similar functionality:

#include <cilk/cilk.h>
#include <iostream>
#include <stdexcept>
#include <string>

// Custom exception classes
class CannotWorkWithException : public std::runtime_error {
public:
    CannotWorkWithException() : std::runtime_error("can't work with 42") {}
};

class OutOfTeaException : public std::runtime_error {
public:
    OutOfTeaException() : std::runtime_error("no more tea available") {}
};

class CannotBoilWaterException : public std::runtime_error {
public:
    CannotBoilWaterException() : std::runtime_error("can't boil water") {}
};

// Function that may throw an exception
int f(int arg) {
    if (arg == 42) {
        throw CannotWorkWithException();
    }
    return arg + 3;
}

// Function that may throw different types of exceptions
void makeTea(int arg) {
    if (arg == 2) {
        throw OutOfTeaException();
    } else if (arg == 4) {
        throw CannotBoilWaterException();
    }
}

int main() {
    for (int i : {7, 42}) {
        try {
            int result = cilk_spawn f(i);
            cilk_sync;
            std::cout << "f worked: " << result << std::endl;
        } catch (const std::exception& e) {
            std::cout << "f failed: " << e.what() << std::endl;
        }
    }

    for (int i = 0; i < 5; ++i) {
        try {
            cilk_spawn makeTea(i);
            cilk_sync;
            std::cout << "Tea is ready!" << std::endl;
        } catch (const OutOfTeaException&) {
            std::cout << "We should buy new tea!" << std::endl;
        } catch (const CannotBoilWaterException&) {
            std::cout << "Now it is dark." << std::endl;
        } catch (const std::exception& e) {
            std::cout << "unknown error: " << e.what() << std::endl;
        }
    }

    return 0;
}

In this Cilk version:

  1. We use custom exception classes to represent different error conditions. These classes inherit from std::runtime_error.

  2. The f function throws a CannotWorkWithException when the input is 42, otherwise it returns the result.

  3. The makeTea function throws different exceptions based on the input.

  4. In the main function, we use try-catch blocks to handle exceptions. This is equivalent to checking for errors in the original code.

  5. We use cilk_spawn and cilk_sync to potentially parallelize the function calls. This is a unique feature of Cilk that allows for easy parallelism.

  6. Instead of using errors.Is, we catch specific exception types. This allows us to handle different error conditions separately.

To compile and run this Cilk program:

$ cilk++ -std=c++11 errors.cpp -o errors
$ ./errors
f worked: 10
f failed: can't work with 42
Tea is ready!
Tea is ready!
We should buy new tea!
Tea is ready!
Now it is dark.

This Cilk version maintains the error handling logic of the original code while adapting it to Cilk’s parallel programming model and C++’s exception handling mechanism.