Browse Source

Initial multi-device OpenCL support for oclvanitygen and oclvanityminer.

master
samr7 12 years ago
parent
commit
af42e55a22
  1. 101
      oclengine.c
  2. 6
      oclengine.h
  3. 54
      oclvanitygen.c
  4. 70
      oclvanityminer.c
  5. 70
      pattern.c
  6. 61
      pattern.h

101
oclengine.c

@ -50,6 +50,7 @@ @@ -50,6 +50,7 @@
#define round_up_pow2(x, a) (((x) + ((a)-1)) & ~((a)-1))
static void vg_ocl_free_args(vg_ocl_context_t *vocp);
static void *vg_opencl_loop(vg_exec_context_t *arg);
/* OpenCL address searching mode */
@ -872,6 +873,7 @@ vg_ocl_init(vg_context_t *vcp, vg_ocl_context_t *vocp, cl_device_id did, @@ -872,6 +873,7 @@ vg_ocl_init(vg_context_t *vcp, vg_ocl_context_t *vocp, cl_device_id did,
memset(vocp, 0, sizeof(*vocp));
vg_exec_context_init(vcp, &vocp->base);
vocp->base.vxc_threadfunc = vg_opencl_loop;
pthread_mutex_init(&vocp->voc_lock, NULL);
pthread_cond_init(&vocp->voc_wait, NULL);
@ -1872,8 +1874,8 @@ out: @@ -1872,8 +1874,8 @@ out:
* Address search thread main loop
*/
void *
vg_opencl_loop(void *arg)
static void *
vg_opencl_loop(vg_exec_context_t *arg)
{
vg_ocl_context_t *vocp = (vg_ocl_context_t *) arg;
int i;
@ -2397,7 +2399,7 @@ get_platform(int num) @@ -2397,7 +2399,7 @@ get_platform(int num)
}
void
enumerate_opencl(void)
vg_ocl_enumerate_devices(void)
{
cl_platform_id *pids;
cl_device_id *dids;
@ -2420,7 +2422,7 @@ enumerate_opencl(void) @@ -2420,7 +2422,7 @@ enumerate_opencl(void)
}
}
cl_device_id
static cl_device_id
get_opencl_device(int platformidx, int deviceidx)
{
cl_platform_id pid;
@ -2432,7 +2434,6 @@ get_opencl_device(int platformidx, int deviceidx) @@ -2432,7 +2434,6 @@ get_opencl_device(int platformidx, int deviceidx)
if (did)
return did;
}
enumerate_opencl();
return NULL;
}
@ -2603,6 +2604,96 @@ out_fail: @@ -2603,6 +2604,96 @@ out_fail:
return NULL;
}
vg_ocl_context_t *
vg_ocl_context_new_from_devstr(vg_context_t *vcp, const char *devstr,
int safemode, int verify)
{
int platformidx, deviceidx;
int worksize = 0, nthreads = 0, nrows = 0, ncols = 0, invsize = 0;
char *dsd, *part, *part2, *save, *param;
dsd = strdup(devstr);
if (!dsd)
return NULL;
save = NULL;
part = strtok_r(dsd, ",", &save);
part2 = strchr(part, ':');
if (!part2) {
fprintf(stderr, "Invalid device specifier '%s'\n", part);
free(dsd);
return NULL;
}
*part2 = '\0';
platformidx = atoi(part);
deviceidx = atoi(part2 + 1);
while ((part = strtok_r(NULL, ",", &save)) != NULL) {
param = strchr(part, '=');
if (!param) {
fprintf(stderr, "Unrecognized parameter '%s'\n", part);
continue;
}
*param = '\0';
param++;
if (!strcmp(part, "grid")) {
ncols = strtol(param, &part2, 0);
if (part2 && *part2 == 'x') {
nrows = strtol(part2+1, NULL, 0);
}
if (!nrows || !ncols) {
fprintf(stderr,
"Invalid grid size '%s'\n", param);
nrows = 0;
ncols = 0;
continue;
}
}
else if (!strcmp(part, "invsize")) {
invsize = atoi(param);
if (!invsize) {
fprintf(stderr,
"Invalid modular inverse size '%s'\n",
param);
continue;
}
if (invsize & (invsize - 1)) {
fprintf(stderr,
"Modular inverse size %d must be "
"a power of 2\n", invsize);
invsize = 0;
continue;
}
}
else if (!strcmp(part, "threads")) {
nthreads = atoi(param);
if (nthreads == 0) {
fprintf(stderr,
"Invalid thread count '%s'\n", optarg);
continue;
}
}
else {
fprintf(stderr, "Unrecognized parameter '%s'\n", part);
}
}
free(dsd);
return vg_ocl_context_new(vcp, platformidx, deviceidx, safemode,
verify, worksize, nthreads, nrows, ncols,
invsize);
}
void
vg_ocl_context_free(vg_ocl_context_t *vocp)
{

6
oclengine.h

@ -30,6 +30,10 @@ extern vg_ocl_context_t *vg_ocl_context_new( @@ -30,6 +30,10 @@ extern vg_ocl_context_t *vg_ocl_context_new(
int invsize);
extern void vg_ocl_context_free(vg_ocl_context_t *vocp);
extern void *vg_opencl_loop(void *vocp);
extern vg_ocl_context_t *vg_ocl_context_new_from_devstr(
vg_context_t *vcp, const char *devstr, int safemode, int verify)
;
extern void vg_ocl_enumerate_devices(void);
#endif /* !defined (__VG_OCLENGINE_H__) */

54
oclvanitygen.c

@ -45,6 +45,11 @@ usage(const char *name) @@ -45,6 +45,11 @@ usage(const char *name)
"location or imported into a bitcoin client to spend any balance received on\n"
"the address.\n"
"By default, <pattern> is interpreted as an exact prefix.\n"
"By default, if no device is specified, and the system has exactly one OpenCL\n"
"device, it will be selected automatically, otherwise if the system has\n"
"multiple OpenCL devices and no device is specified, an error will be\n"
"reported. To use multiple devices simultaneously, specify the -D option for\n"
"each device.\n"
"\n"
"Options:\n"
"-v Verbose output\n"
@ -58,6 +63,9 @@ usage(const char *name) @@ -58,6 +63,9 @@ usage(const char *name)
"-E <password> Encrypt private keys with <password> (UNSAFE)\n"
"-p <platform> Select OpenCL platform\n"
"-d <device> Select OpenCL device\n"
"-D <devstr> Use OpenCL device, identified by device string\n"
" Form: <platform>:<devicenumber>[,<options>]\n"
" Example: 0:0,grid=1024x1024\n"
"-S Safe mode, disable OpenCL loop unrolling optimizations\n"
"-w <worksize> Set work items per thread in a work unit\n"
"-t <threads> Set target thread count per multiprocessor\n"
@ -71,6 +79,8 @@ usage(const char *name) @@ -71,6 +79,8 @@ usage(const char *name)
version, name);
}
#define MAX_DEVS 32
int
main(int argc, char **argv)
{
@ -99,9 +109,12 @@ main(int argc, char **argv) @@ -99,9 +109,12 @@ main(int argc, char **argv)
EC_POINT *pubkey_base = NULL;
const char *result_file = NULL;
const char *key_password = NULL;
char *devstrs[MAX_DEVS];
int ndevstrs = 0;
int opened = 0;
while ((opt = getopt(argc, argv,
"vqikNTX:eE:p:P:d:w:t:g:b:VSh?f:o:s:")) != -1) {
"vqikNTX:eE:p:P:d:w:t:g:b:VSh?f:o:s:D:")) != -1) {
switch (opt) {
case 'v':
verbose = 2;
@ -188,6 +201,15 @@ main(int argc, char **argv) @@ -188,6 +201,15 @@ main(int argc, char **argv)
case 'S':
safe_mode = 1;
break;
case 'D':
if (ndevstrs >= MAX_DEVS) {
fprintf(stderr,
"Too many OpenCL devices (limit %d)\n",
MAX_DEVS);
return 1;
}
devstrs[ndevstrs++] = optarg;
break;
case 'P': {
if (pubkey_base != NULL) {
fprintf(stderr,
@ -338,14 +360,38 @@ main(int argc, char **argv) @@ -338,14 +360,38 @@ main(int argc, char **argv)
fprintf(stderr,
"Regular expressions: %ld\n", vcp->vc_npatterns);
if (ndevstrs) {
for (opt = 0; opt < ndevstrs; opt++) {
vocp = vg_ocl_context_new_from_devstr(vcp, devstrs[opt],
safe_mode,
verify_mode);
if (!vocp) {
fprintf(stderr,
"Could not open device '%s', ignoring\n",
devstrs[opt]);
} else {
opened++;
}
}
} else {
vocp = vg_ocl_context_new(vcp, platformidx, deviceidx,
safe_mode, verify_mode,
worksize, nthreads, nrows, ncols, invsize);
if (!vocp) {
worksize, nthreads,
nrows, ncols, invsize);
if (vocp)
opened++;
}
if (!opened) {
vg_ocl_enumerate_devices();
return 1;
}
vg_opencl_loop(vocp);
opt = vg_context_start_threads(vcp);
if (opt)
return 1;
vg_context_wait_for_completion(vcp);
vg_ocl_context_free(vocp);
return 0;
}

70
oclvanityminer.c

@ -735,6 +735,11 @@ usage(const char *name) @@ -735,6 +735,11 @@ usage(const char *name)
"generate the address with the best difficulty to reward ratio. Maintains\n"
"contact with the bounty pool server and periodically refreshes the bounty\n"
"list.\n"
"By default, if no device is specified, and the system has exactly one OpenCL\n"
"device, it will be selected automatically, otherwise if the system has\n"
"multiple OpenCL devices and no device is specified, an error will be\n"
"reported. To use multiple devices simultaneously, specify the -D option for\n"
"each device.\n"
"\n"
"Options:\n"
"-u <URL> Bounty pool URL\n"
@ -744,6 +749,9 @@ usage(const char *name) @@ -744,6 +749,9 @@ usage(const char *name)
"-q Quiet output\n"
"-p <platform> Select OpenCL platform\n"
"-d <device> Select OpenCL device\n"
"-D <devstr> Use OpenCL device, identified by device string\n"
" Form: <platform>:<devicenumber>[,<options>]\n"
" Example: 0:0,grid=1024x1024\n"
"-S Safe mode, disable OpenCL loop unrolling optimizations\n"
"-w <worksize> Set work items per thread in a work unit\n"
"-t <threads> Set target thread count per multiprocessor\n"
@ -753,6 +761,7 @@ usage(const char *name) @@ -753,6 +761,7 @@ usage(const char *name)
version, name);
}
#define MAX_DEVS 32
int
main(int argc, char **argv)
@ -770,12 +779,15 @@ main(int argc, char **argv) @@ -770,12 +779,15 @@ main(int argc, char **argv)
int invsize = 0;
int verify_mode = 0;
int safe_mode = 0;
char *devstrs[MAX_DEVS];
int ndevstrs = 0;
vg_context_t *vcp = NULL;
vg_ocl_context_t *vocp = NULL;
int res;
int thread_started = 0;
pthread_t thread;
pubkeybatch_t *active_pkb = NULL;
server_context_t *scp = NULL;
@ -794,7 +806,7 @@ main(int argc, char **argv) @@ -794,7 +806,7 @@ main(int argc, char **argv)
}
while ((opt = getopt(argc, argv,
"u:a:vqp:d:w:t:g:b:VSh?i:")) != -1) {
"u:a:vqp:d:w:t:g:b:VD:Sh?i:")) != -1) {
switch (opt) {
case 'u':
url = optarg;
@ -871,6 +883,15 @@ main(int argc, char **argv) @@ -871,6 +883,15 @@ main(int argc, char **argv)
case 'S':
safe_mode = 1;
break;
case 'D':
if (ndevstrs >= MAX_DEVS) {
fprintf(stderr,
"Too many OpenCL devices (limit %d)\n",
MAX_DEVS);
return 1;
}
devstrs[ndevstrs++] = optarg;
break;
default:
usage(argv[0]);
return 1;
@ -915,6 +936,34 @@ main(int argc, char **argv) @@ -915,6 +936,34 @@ main(int argc, char **argv)
if (server_context_getwork(scp))
return 1;
/* Set up OpenCL */
res = 0;
if (ndevstrs) {
for (opt = 0; opt < ndevstrs; opt++) {
vocp = vg_ocl_context_new_from_devstr(vcp, devstrs[opt],
safe_mode,
verify_mode);
if (!vocp) {
fprintf(stderr,
"Could not open device '%s', ignoring\n",
devstrs[opt]);
} else {
res++;
}
}
} else {
vocp = vg_ocl_context_new(vcp, platformidx, deviceidx,
safe_mode, verify_mode,
worksize, nthreads,
nrows, ncols, invsize);
if (vocp)
res++;
}
if (!res) {
vg_ocl_enumerate_devices();
return 1;
}
while (1) {
if (avl_root_empty(&scp->items))
server_context_getwork(scp);
@ -929,10 +978,8 @@ main(int argc, char **argv) @@ -929,10 +978,8 @@ main(int argc, char **argv)
if (thread_started && (!active_pkb || (pkb != active_pkb))) {
/* If a thread is running, stop it */
vcp->vc_halt = 1;
pthread_join(thread, NULL);
vg_context_stop_threads(vcp);
thread_started = 0;
vcp->vc_halt = 0;
if (active_pkb) {
check_solution(scp, active_pkb);
active_pkb = NULL;
@ -970,18 +1017,9 @@ main(int argc, char **argv) @@ -970,18 +1017,9 @@ main(int argc, char **argv)
assert(vcp->vc_npatterns);
}
if (!vocp) {
vocp = vg_ocl_context_new(vcp,
platformidx, deviceidx,
safe_mode, verify_mode,
worksize, nthreads, nrows,
ncols, invsize);
if (!vocp)
res = vg_context_start_threads(vcp);
if (res)
return 1;
}
res = pthread_create(&thread, NULL,
vg_opencl_loop, vocp);
thread_started = 1;
active_pkb = pkb;
}

70
pattern.c

@ -54,16 +54,14 @@ static pthread_mutex_t vg_thread_lock = PTHREAD_MUTEX_INITIALIZER; @@ -54,16 +54,14 @@ static pthread_mutex_t vg_thread_lock = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t vg_thread_rdcond = PTHREAD_COND_INITIALIZER;
static pthread_cond_t vg_thread_wrcond = PTHREAD_COND_INITIALIZER;
static pthread_cond_t vg_thread_upcond = PTHREAD_COND_INITIALIZER;
static vg_exec_context_t *vg_threads = NULL;
static int vg_thread_excl = 0;
static void
__vg_exec_context_yield(vg_exec_context_t *vxcp)
{
vxcp->vxc_lockmode = 0;
while (vg_thread_excl) {
while (vxcp->vxc_vc->vc_thread_excl) {
if (vxcp->vxc_stop) {
assert(vg_thread_excl);
assert(vxcp->vxc_vc->vc_thread_excl);
vxcp->vxc_stop = 0;
pthread_cond_signal(&vg_thread_upcond);
}
@ -78,6 +76,7 @@ int @@ -78,6 +76,7 @@ int
vg_exec_context_upgrade_lock(vg_exec_context_t *vxcp)
{
vg_exec_context_t *tp;
vg_context_t *vcp;
if (vxcp->vxc_lockmode == 2)
return 0;
@ -86,20 +85,21 @@ vg_exec_context_upgrade_lock(vg_exec_context_t *vxcp) @@ -86,20 +85,21 @@ vg_exec_context_upgrade_lock(vg_exec_context_t *vxcp)
assert(vxcp->vxc_lockmode == 1);
vxcp->vxc_lockmode = 0;
vcp = vxcp->vxc_vc;
if (vg_thread_excl++) {
if (vcp->vc_thread_excl++) {
assert(vxcp->vxc_stop);
vxcp->vxc_stop = 0;
pthread_cond_signal(&vg_thread_upcond);
pthread_cond_wait(&vg_thread_wrcond, &vg_thread_lock);
for (tp = vg_threads; tp != NULL; tp = tp->vxc_next) {
for (tp = vcp->vc_threads; tp != NULL; tp = tp->vxc_next) {
assert(!tp->vxc_lockmode);
assert(!tp->vxc_stop);
}
} else {
for (tp = vg_threads; tp != NULL; tp = tp->vxc_next) {
for (tp = vcp->vc_threads; tp != NULL; tp = tp->vxc_next) {
if (tp->vxc_lockmode) {
assert(tp->vxc_lockmode != 2);
tp->vxc_stop = 1;
@ -107,7 +107,9 @@ vg_exec_context_upgrade_lock(vg_exec_context_t *vxcp) @@ -107,7 +107,9 @@ vg_exec_context_upgrade_lock(vg_exec_context_t *vxcp)
}
do {
for (tp = vg_threads; tp != NULL; tp = tp->vxc_next) {
for (tp = vcp->vc_threads;
tp != NULL;
tp = tp->vxc_next) {
if (tp->vxc_lockmode) {
assert(tp->vxc_lockmode != 2);
pthread_cond_wait(&vg_thread_upcond,
@ -127,10 +129,9 @@ void @@ -127,10 +129,9 @@ void
vg_exec_context_downgrade_lock(vg_exec_context_t *vxcp)
{
pthread_mutex_lock(&vg_thread_lock);
assert(vxcp->vxc_lockmode == 2);
assert(!vxcp->vxc_stop);
if (!--vg_thread_excl) {
if (!--vxcp->vxc_vc->vc_thread_excl) {
vxcp->vxc_lockmode = 1;
pthread_cond_broadcast(&vg_thread_rdcond);
pthread_mutex_unlock(&vg_thread_lock);
@ -166,8 +167,8 @@ vg_exec_context_init(vg_context_t *vcp, vg_exec_context_t *vxcp) @@ -166,8 +167,8 @@ vg_exec_context_init(vg_context_t *vcp, vg_exec_context_t *vxcp)
vxcp->vxc_lockmode = 0;
vxcp->vxc_stop = 0;
vxcp->vxc_next = vg_threads;
vg_threads = vxcp;
vxcp->vxc_next = vcp->vc_threads;
vcp->vc_threads = vxcp;
__vg_exec_context_yield(vxcp);
pthread_mutex_unlock(&vg_thread_lock);
return 1;
@ -185,7 +186,7 @@ vg_exec_context_del(vg_exec_context_t *vxcp) @@ -185,7 +186,7 @@ vg_exec_context_del(vg_exec_context_t *vxcp)
assert(vxcp->vxc_lockmode == 1);
vxcp->vxc_lockmode = 0;
for (pprev = &vg_threads, tp = *pprev;
for (pprev = &vxcp->vxc_vc->vc_threads, tp = *pprev;
(tp != vxcp) && (tp != NULL);
pprev = &tp->vxc_next, tp = *pprev);
@ -634,6 +635,49 @@ vg_context_hash160_sort(vg_context_t *vcp, void *buf) @@ -634,6 +635,49 @@ vg_context_hash160_sort(vg_context_t *vcp, void *buf)
return vcp->vc_hash160_sort(vcp, buf);
}
int
vg_context_start_threads(vg_context_t *vcp)
{
vg_exec_context_t *vxcp;
int res;
for (vxcp = vcp->vc_threads; vxcp != NULL; vxcp = vxcp->vxc_next) {
res = pthread_create((pthread_t *) &vxcp->vxc_pthread,
NULL,
(void *(*)(void *)) vxcp->vxc_threadfunc,
vxcp);
if (res) {
fprintf(stderr, "ERROR: could not create thread: %d\n",
res);
vg_context_stop_threads(vcp);
return -1;
}
vxcp->vxc_thread_active = 1;
}
return 0;
}
void
vg_context_stop_threads(vg_context_t *vcp)
{
vcp->vc_halt = 1;
vg_context_wait_for_completion(vcp);
vcp->vc_halt = 0;
}
void
vg_context_wait_for_completion(vg_context_t *vcp)
{
vg_exec_context_t *vxcp;
for (vxcp = vcp->vc_threads; vxcp != NULL; vxcp = vxcp->vxc_next) {
if (!vxcp->vxc_thread_active)
continue;
pthread_join((pthread_t) vxcp->vxc_pthread, NULL);
vxcp->vxc_thread_active = 0;
}
}
/*
* Find the bignum ranges that produce a given prefix.

61
pattern.h

@ -22,6 +22,8 @@ @@ -22,6 +22,8 @@
#include <openssl/bn.h>
#include <openssl/ec.h>
#include <pthread.h>
#ifdef _WIN32
#include "winglue.h"
#else
@ -35,11 +37,15 @@ @@ -35,11 +37,15 @@
#define VANITYGEN_VERSION "0.20pre"
typedef struct _vg_context_s vg_context_t;
struct _vg_exec_context_s;
typedef struct _vg_exec_context_s vg_exec_context_t;
typedef void *(*vg_exec_context_threadfunc_t)(vg_exec_context_t *);
/* Context of one pattern-matching unit within the process */
typedef struct _vg_exec_context_s {
struct _vg_exec_context_s {
vg_context_t *vxc_vc;
BN_CTX *vxc_bnctx;
EC_KEY *vxc_key;
@ -50,23 +56,15 @@ typedef struct _vg_exec_context_s { @@ -50,23 +56,15 @@ typedef struct _vg_exec_context_s {
BIGNUM vxc_bntmp;
BIGNUM vxc_bntmp2;
vg_exec_context_threadfunc_t vxc_threadfunc;
pthread_t vxc_pthread;
int vxc_thread_active;
/* Thread synchronization */
struct _vg_exec_context_s *vxc_next;
int vxc_lockmode;
int vxc_stop;
} vg_exec_context_t;
/* Init/cleanup for common execution context */
extern int vg_exec_context_init(vg_context_t *vcp, vg_exec_context_t *vxcp);
extern void vg_exec_context_del(vg_exec_context_t *vxcp);
extern void vg_exec_context_consolidate_key(vg_exec_context_t *vxcp);
extern void vg_exec_context_calc_address(vg_exec_context_t *vxcp);
extern EC_KEY *vg_exec_context_new_key(void);
/* Execution context lock handling functions */
extern void vg_exec_context_downgrade_lock(vg_exec_context_t *vxcp);
extern int vg_exec_context_upgrade_lock(vg_exec_context_t *vxcp);
extern void vg_exec_context_yield(vg_exec_context_t *vxcp);
};
typedef void (*vg_free_func_t)(vg_context_t *);
@ -106,6 +104,9 @@ struct _vg_context_s { @@ -106,6 +104,9 @@ struct _vg_context_s {
EC_POINT *vc_pubkey_base;
int vc_halt;
vg_exec_context_t *vc_threads;
int vc_thread_excl;
/* Internal methods */
vg_free_func_t vc_free;
vg_add_pattern_func_t vc_add_patterns;
@ -126,26 +127,48 @@ struct _vg_context_s { @@ -126,26 +127,48 @@ struct _vg_context_s {
};
/* Base context methods */
extern void vg_context_free(vg_context_t *vcp);
extern int vg_context_add_patterns(vg_context_t *vcp,
const char ** const patterns, int npatterns);
extern void vg_context_clear_all_patterns(vg_context_t *vcp);
extern int vg_context_hash160_sort(vg_context_t *vcp, void *buf);
extern void vg_context_thread_exit(vg_context_t *vcp);
extern int vg_context_start_threads(vg_context_t *vcp);
extern void vg_context_stop_threads(vg_context_t *vcp);
extern void vg_context_wait_for_completion(vg_context_t *vcp);
/* Prefix context methods */
extern vg_context_t *vg_prefix_context_new(int addrtype, int privtype,
int caseinsensitive);
extern double vg_prefix_get_difficulty(int addrtype, const char *pattern);
/* Regex context methods */
extern vg_context_t *vg_regex_context_new(int addrtype, int privtype);
/* Utility functions */
extern int vg_output_timing(vg_context_t *vcp, int cycle, struct timeval *last);
extern void vg_output_match_console(vg_context_t *vcp, EC_KEY *pkey,
const char *pattern);
extern void vg_output_timing_console(vg_context_t *vcp, double count,
unsigned long long rate,
unsigned long long total);
/* Internal vg_context methods */
extern int vg_context_hash160_sort(vg_context_t *vcp, void *buf);
extern void vg_context_thread_exit(vg_context_t *vcp);
/* Internal Init/cleanup for common execution context */
extern int vg_exec_context_init(vg_context_t *vcp, vg_exec_context_t *vxcp);
extern void vg_exec_context_del(vg_exec_context_t *vxcp);
extern void vg_exec_context_consolidate_key(vg_exec_context_t *vxcp);
extern void vg_exec_context_calc_address(vg_exec_context_t *vxcp);
extern EC_KEY *vg_exec_context_new_key(void);
/* Internal execution context lock handling functions */
extern void vg_exec_context_downgrade_lock(vg_exec_context_t *vxcp);
extern int vg_exec_context_upgrade_lock(vg_exec_context_t *vxcp);
extern void vg_exec_context_yield(vg_exec_context_t *vxcp);
#endif /* !defined (__VG_PATTERN_H__) */

Loading…
Cancel
Save