В Sat, 29 Apr 2023 00:00:02 +0000 Dario Strbenac via R-devel <r-devel@r-project.org> пишет:
> Could save.image() be redesigned so that it promptly responds to > Ctrl+C? It prevents the command line from being used for a number of > hours if the contents of the workspace are large. This is ultimately caused by serialize() being non-interruptible. A relatively simple way to hang an R session for a long-ish time would therefore be: f <- xzfile(nullfile(), 'a+b') x <- rep(0, 1e9) # approx. 8 gigabytes, adjust for your RAM size serialize(x, f) close(f) This means that calling R_CheckUserInterrupt() between saving individual objects is not enough: R also needs to check for interrupts while saving sufficiently long vectors. Since the serialize() infrastructure is carefully written to avoid resource leaks on allocation failures, it looks relatively safe to liberally sprinkle R_CheckUserInterrupt() where it makes sense to do so, i.e. once per WriteItem() (which calls itself recursively and non-recursively) and once per every downstream for loop iteration. Valgrind doesn't show any new leaks if I apply the patch, interrupt serialize() and then exit. R also passes make check after the applied patch. Do these changes make sense, or am I overlooking some other problem? -- Best regards, Ivan
--- src/main/serialize.c (revision 84337) +++ src/main/serialize.c (working copy) @@ -879,8 +879,10 @@ R_xlen_t len = XLENGTH(s); OutInteger(stream, 0); /* place holder to allow names if we want to */ WriteLENGTH(stream, s); - for (R_xlen_t i = 0; i < len; i++) + for (R_xlen_t i = 0; i < len; i++) { + R_CheckUserInterrupt(); WriteItem(STRING_ELT(s, i), ref_table, stream); + } } #include <rpc/types.h> @@ -901,6 +903,7 @@ R_xlen_t done, this; XDR xdrs; for (done = 0; done < length; done += this) { + R_CheckUserInterrupt(); this = min2(CHUNK_SIZE, length - done); xdrmem_create(&xdrs, buf, (int)(this * sizeof(int)), XDR_ENCODE); for(int cnt = 0; cnt < this; cnt++) @@ -916,6 +919,7 @@ /* write in chunks to avoid overflowing ints */ R_xlen_t done, this; for (done = 0; done < length; done += this) { + R_CheckUserInterrupt(); this = min2(CHUNK_SIZE, length - done); stream->OutBytes(stream, INTEGER(s) + done, (int)(sizeof(int) * this)); @@ -923,8 +927,10 @@ break; } default: - for (R_xlen_t cnt = 0; cnt < length; cnt++) + for (R_xlen_t cnt = 0; cnt < length; cnt++) { OutInteger(stream, INTEGER(s)[cnt]); + R_CheckUserInterrupt(); + } } } @@ -938,6 +944,7 @@ R_xlen_t done, this; XDR xdrs; for (done = 0; done < length; done += this) { + R_CheckUserInterrupt(); this = min2(CHUNK_SIZE, length - done); xdrmem_create(&xdrs, buf, (int)(this * sizeof(double)), XDR_ENCODE); for(int cnt = 0; cnt < this; cnt++) @@ -952,6 +959,7 @@ { R_xlen_t done, this; for (done = 0; done < length; done += this) { + R_CheckUserInterrupt(); this = min2(CHUNK_SIZE, length - done); stream->OutBytes(stream, REAL(s) + done, (int)(sizeof(double) * this)); @@ -959,8 +967,10 @@ break; } default: - for (R_xlen_t cnt = 0; cnt < length; cnt++) + for (R_xlen_t cnt = 0; cnt < length; cnt++) { + R_CheckUserInterrupt(); OutReal(stream, REAL(s)[cnt]); + } } } @@ -975,6 +985,7 @@ XDR xdrs; Rcomplex *c = COMPLEX(s); for (done = 0; done < length; done += this) { + R_CheckUserInterrupt(); this = min2(CHUNK_SIZE, length - done); xdrmem_create(&xdrs, buf, (int)(this * sizeof(Rcomplex)), XDR_ENCODE); for(int cnt = 0; cnt < this; cnt++) { @@ -991,6 +1002,7 @@ { R_xlen_t done, this; for (done = 0; done < length; done += this) { + R_CheckUserInterrupt(); this = min2(CHUNK_SIZE, length - done); stream->OutBytes(stream, COMPLEX(s) + done, (int)(sizeof(Rcomplex) * this)); @@ -998,8 +1010,10 @@ break; } default: - for (R_xlen_t cnt = 0; cnt < length; cnt++) + for (R_xlen_t cnt = 0; cnt < length; cnt++) { + R_CheckUserInterrupt(); OutComplex(stream, COMPLEX(s)[cnt]); + } } } @@ -1034,6 +1048,7 @@ tailcall: R_CheckStack(); + R_CheckUserInterrupt(); if (ALTREP(s) && stream->version >= 3) { SEXP info = ALTREP_SERIALIZED_CLASS(s); SEXP state = ALTREP_SERIALIZED_STATE(s); @@ -1186,15 +1201,19 @@ case STRSXP: len = XLENGTH(s); WriteLENGTH(stream, s); - for (R_xlen_t ix = 0; ix < len; ix++) + for (R_xlen_t ix = 0; ix < len; ix++) { + R_CheckUserInterrupt(); WriteItem(STRING_ELT(s, ix), ref_table, stream); + } break; case VECSXP: case EXPRSXP: len = XLENGTH(s); WriteLENGTH(stream, s); - for (R_xlen_t ix = 0; ix < len; ix++) + for (R_xlen_t ix = 0; ix < len; ix++) { + R_CheckUserInterrupt(); WriteItem(VECTOR_ELT(s, ix), ref_table, stream); + } break; case BCODESXP: WriteBC(s, ref_table, stream); @@ -1208,6 +1227,7 @@ { R_xlen_t done, this; for (done = 0; done < len; done += this) { + R_CheckUserInterrupt(); this = min2(CHUNK_SIZE, len - done); stream->OutBytes(stream, RAW(s) + done, (int) this); } @@ -1214,8 +1234,10 @@ break; } default: - for (R_xlen_t ix = 0; ix < len; ix++) + for (R_xlen_t ix = 0; ix < len; ix++) { + R_CheckUserInterrupt(); OutByte(stream, RAW(s)[ix]); + } } break; case S4SXP:
______________________________________________ R-devel@r-project.org mailing list https://stat.ethz.ch/mailman/listinfo/r-devel