Skip to content

Commit

Permalink
At compile time, packages linking to 'ergm' will now store the C API …
Browse files Browse the repository at this point in the history
…version numbers of the 'ergm' installation they were built against in global variables built_ERGM_API_MAJOR and built_ERGM_API_MINOR; model and proposal initialization code will then check whether the linking package was compiled under a wrong version and issue a warning.
  • Loading branch information
krivit committed Jan 9, 2025
1 parent 94afe20 commit 9f9ddc2
Show file tree
Hide file tree
Showing 7 changed files with 67 additions and 0 deletions.
2 changes: 2 additions & 0 deletions inst/include/ergm_Rutil.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,4 +71,6 @@ static inline void *R_calloc_helper(size_t n, size_t size){

#define R_CheckUserInterruptEvery(freq, iter) if((iter)%(freq) == 0) R_CheckUserInterrupt();

void warn_API_version(const char *sn);

#endif
15 changes: 15 additions & 0 deletions inst/include/ergm_stubs.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@
* Copyright 2003-2024 Statnet Commons
*/

#include "ergm_constants.h"

unsigned int built_ERGM_API_MAJOR = ERGM_API_MAJOR;
unsigned int built_ERGM_API_MINOR = ERGM_API_MINOR;

#define STUBFILE
#include <stddef.h>
#include <R_ext/Rdynload.h>
Expand Down Expand Up @@ -77,6 +82,16 @@ static void (*fun)(Vertex,Vertex,double,void *,WtNetwork *,double) = NULL;
if(fun==NULL) fun = (void (*)(Vertex,Vertex,double,void *,WtNetwork *,double)) R_FindSymbol("WtDyadGenUpdate", "ergm", NULL);
fun(tail,head,weight,gen,nwp,edgestate);
}
void AddOnDyadGenInit(OnDyadGenInit callback, void *payload){
static void (*fun)(OnDyadGenInit,void *) = NULL;
if(fun==NULL) fun = (void (*)(OnDyadGenInit,void *)) R_FindSymbol("AddOnDyadGenInit", "ergm", NULL);
fun(callback,payload);
}
void DeleteOnDyadGenInit(OnDyadGenInit callback, void *payload){
static void (*fun)(OnDyadGenInit,void *) = NULL;
if(fun==NULL) fun = (void (*)(OnDyadGenInit,void *)) R_FindSymbol("DeleteOnDyadGenInit", "ergm", NULL);
fun(callback,payload);
}

#define STUBFILE
#include <stddef.h>
Expand Down
4 changes: 4 additions & 0 deletions src/MHproposal.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ MHProposal *MHProposalInitialize(SEXP pR, Network *nwp, void **aux_storage){
/* Extract the required string information from the relevant sources */
const char *fname = FIRSTCHAR(getListElement(pR, "name")),
*sn = FIRSTCHAR(getListElement(pR, "pkgname"));

/* Check if the package is compiled against a different version of 'ergm'. */
warn_API_version(sn);

char *fn = R_Calloc(strlen(fname)+4, char);
fn[0]='M';
fn[1]='H';
Expand Down
34 changes: 34 additions & 0 deletions src/ergm_Rutil.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#include "ergm_Rutil.h"
#include "ergm_constants.h"
#include "ergm_kvec.h"
#include "ergm_khash.h"

unsigned int built_ERGM_API_MAJOR = ERGM_API_MAJOR;
unsigned int built_ERGM_API_MINOR = ERGM_API_MINOR;

static kvec_t(khint_t) build_version_warned = kv_blank;

void warn_API_version(const char *sn){

/* Here, we are saving the hash of the library name rather than the
name itself, since keeping track of strings allocated from the
heap is a hassle, and we don't want to assume that sn will always
be around. Chances of collision are negligible. */
khint_t snhash = kh_str_hash_func(sn);
unsigned int i = 0;
while(i < kv_size(build_version_warned) && kv_A(build_version_warned, i) != snhash) i++;
if(i < kv_size(build_version_warned)) return;

kv_push(khint_t, build_version_warned, snhash);

void *bv = R_FindSymbol("built_ERGM_API_MAJOR",sn,NULL);
unsigned int bv_major = bv ? *(unsigned int *)bv : 0;
if(bv_major){
unsigned int bv_minor = *(unsigned int *)R_FindSymbol("built_ERGM_API_MINOR",sn,NULL);
if(bv_major != ERGM_API_MAJOR || bv_minor != ERGM_API_MINOR)
warningcall_immediate(R_NilValue, "Package '%s' was compiled against 'ergm' with C API version\n %d.%d, but it is being loaded by 'ergm' with C API version %d.%d. Inconsistent\n versions may result in malfunctions ranging from incorrect results to R\n crashing. Please rebuild the package against the current 'ergm' version.",
sn, bv_major, bv_minor, ERGM_API_MAJOR, ERGM_API_MINOR);
}else
warningcall_immediate(R_NilValue, "Package '%s' was compiled against an older version of 'ergm'\n that did not store '%s''s C API version information. Inconsistent\n versions may cause malfunctions ranging from incorrect results to R\n crashing. Please rebuild the package against the current 'ergm' version.",
sn, sn);
}
4 changes: 4 additions & 0 deletions src/model.c
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,10 @@ Model* ModelInitialize(SEXP mR, SEXP ext_state, Network *nwp, Rboolean noinit_s)
the respective functions.*/
const char *fname = FIRSTCHAR(getListElement(thisterm->R, "name")),
*sn = FIRSTCHAR(getListElement(thisterm->R, "pkgname"));

/* Check if the package is compiled against a different version of 'ergm'. */
warn_API_version(sn);

/* Extract the required string information from the relevant sources */
char *fn = R_Calloc(strlen(fname)+3, char);
fn[1]='_';
Expand Down
4 changes: 4 additions & 0 deletions src/wtMHproposal.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ WtMHProposal *WtMHProposalInitialize(SEXP pR, WtNetwork *nwp, void **aux_storage
/* Extract the required string information from the relevant sources */
const char *fname = FIRSTCHAR(getListElement(pR, "name")),
*sn = FIRSTCHAR(getListElement(pR, "pkgname"));

/* Check if the package is compiled against a different version of 'ergm'. */
warn_API_version(sn);

char *fn = R_Calloc(strlen(fname)+4, char);
fn[0]='M';
fn[1]='H';
Expand Down
4 changes: 4 additions & 0 deletions src/wtmodel.c
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,10 @@ WtModel* WtModelInitialize (SEXP mR, SEXP ext_state, WtNetwork *nwp, Rboolean no
the respective functions.*/
const char *fname = FIRSTCHAR(getListElement(thisterm->R, "name")),
*sn = FIRSTCHAR(getListElement(thisterm->R, "pkgname"));

/* Check if the package is compiled against a different version of 'ergm'. */
warn_API_version(sn);

/* Extract the required string information from the relevant sources */
char *fn = R_Calloc(strlen(fname)+3, char);
fn[1]='_';
Expand Down

0 comments on commit 9f9ddc2

Please sign in to comment.