В 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

Reply via email to