File: piWithInterrupts.cpp

package info (click to toggle)
rcpp 1.1.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 7,480 kB
  • sloc: cpp: 27,436; ansic: 7,778; sh: 53; makefile: 2
file content (134 lines) | stat: -rw-r--r-- 3,175 bytes parent folder | download | duplicates (9)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
// -*- mode: C++; c-indent-level: 4; c-basic-offset: 4; tab-width: 8 -*-

#include <Rcpp.h>

#ifdef _OPENMP
#include <omp.h>
#endif

#include <R_ext/Utils.h>

/**
 * Base class for interrupt exceptions thrown when user
 * interrupts are detected.
 */
class interrupt_exception : public std::exception {
public:
    /**
     * Constructor.
     * @param[in] message A description of event that
     *  caused this exception.
     */
    interrupt_exception(std::string message)
	: detailed_message(message)
    {};

    /**
     * Virtual destructor. Needed to avoid "looser throw specification" errors.
     */
    virtual ~interrupt_exception() throw() {};

    /**
     * Obtain a description of the exception.
     * @return Description.
     */
    virtual const char* what() const throw() {
	return detailed_message.c_str();
    }

    /**
     * String with details on the error.
     */
    std::string detailed_message;
};

/**
 * Do the actual check for an interrupt.
 * @attention This method should never be called directly.
 * @param[in] dummy Dummy argument.
 */
static inline void check_interrupt_impl(void* /*dummy*/) {
    R_CheckUserInterrupt();
}

/**
 * Call this method to check for user interrupts.
 * This is based on the results of a discussion on the
 * R-devel mailing list, suggested by Simon Urbanek.
 * @attention This method must not be called by any other
 *  thread than the master thread. If called from within
 *  an OpenMP parallel for loop, make sure to check
 *  for omp_get_thread_num()==0 before calling this method!
 * @return True, if a user interrupt has been detected.
 */
inline bool check_interrupt() {
    return (R_ToplevelExec(check_interrupt_impl, NULL) == FALSE);
}

/**
 * Compute pi using the Leibniz formula
 * (a very inefficient approach).
 * @param[in] n Number of summands
 * @param[in] frequency Check for interrupts after
 *  every @p frequency loop cycles.
 */
RcppExport SEXP PiLeibniz(SEXP n, SEXP frequency)
{
    BEGIN_RCPP

    // cast parameters
    int n_cycles = Rcpp::as<int>(n);
    int interrupt_check_frequency = Rcpp::as<int>(frequency);

    // user interrupt flag
    bool interrupt = false;

    double pi = 0;
#ifdef _OPENMP
#pragma omp parallel for \
    shared(interrupt_check_frequency, n_cycles, interrupt)	\
    reduction(+:pi)
#endif
    for (int i=0; i<n_cycles; i+=interrupt_check_frequency) {
	// check for user interrupt
	if (interrupt) {
	    continue;
	}

#ifdef _OPENMP
	if (omp_get_thread_num() == 0) // only in master thread!
#endif
	    if (check_interrupt()) {
		interrupt = true;
	    }

	// do actual computations
	int j_end = std::min(i+interrupt_check_frequency, n_cycles);
	for (int j=i; j<j_end; ++j) {
	    double summand = 1.0 / (double)(2*j + 1);
	    if (j % 2 == 0) {
		pi += summand;
	    }
	    else {
		pi -= summand;
	    }
	}
    }

    // additional check, in case frequency was too large
    if (check_interrupt()) {
	interrupt = true;
    }

    // throw exception if interrupt occurred
    if (interrupt) {
	throw interrupt_exception("The computation of pi was interrupted.");
    }

    pi *= 4.0;

    // result list
    return Rcpp::wrap(pi);

    END_RCPP
}