Autosave in your program

Hi.
Just like me you might want some code in your tool that saves the file you are working on in a set interval. I searched the forum but there was no topic like this before.

A little research in the java documentation as well as on stackoverflow and I found this piece of code:

import java.util.*;

class SayHello extends TimerTask {
    public void run() {
       System.out.println("Hello World!"); 
    }
}

// And From your main() method or any other method
Timer timer = new Timer();
timer.schedule(new SayHello(), 0, 5000);

Source: https://stackoverflow.com/questions/12908412/print-hello-world-every-x-seconds

My idea is to replace the “Hello World” part with my code to save the file that is currently opened in my program.

However it might throw an error when I am writing or reading from that file at the exact same moment. So I will have to make a key that is only released when it is save to save the file (no pun intended).

Also I don’t want to save every 5 seconds but more like every 15 minutes. So I will have to replace the 5000ms with 900 000ms.

Did you write an autosave function before? What are your ideas to save a file at a specific rate? Can you find ways to improve what I found?

Edit:
This is closer to what I will use.

import java.util.*;
Timer timer = new Timer();

int autosave_state = 0;

void setup() {
  timer.schedule(new Autosave(), 0, 900000); // every 15 min
}


void draw() {
  while (autosave_state == 2) {
    // wait until save is over
  }
  
  autosave_state = 0;
  // do critical stuff
  autosave_state = 1;
  // non critical stuff
}

class Autosave extends TimerTask {
    public void run() {
       while (autosave_state != 1) {
         // wait until critical stuff is over
       }
       
       autosave_state = 2;
       // save here
       autosave_state = 3;
    }
}

Is your program using threads, what kind of data / file handle, what kind of saving (total / progressive), and how are you writing to your file?

If you are in a normal single-threaded program (not using Thread) for example, and if you are using saveStrings / saveBytes / saveJSON etc, then I don’t think you have to do any of this.

However: threading experts like @neilcsmith and @GoToLoop may know better than I do.

If I am correct, you could just write:

Edit: I was NOT correct, correction below

int autoSaveDuration;
int nextSave;
void setup(){
  autoSaveDuration = 1000 * 5; // save every 5 seconds
  nextSave = autoSaveDuration;
}
void draw(){
  if(millis()>nextSave){
    mySave();
    println("saved at " + millis());
    nextSave += autoSaveDuration;
  }
}
void mySave(){
}

…and it will run your save safely every 5 seconds (or 100 seconds, or whatever you choose).

The key thing here is that Processing already has an internal timer, so you don’t actually need to create a heavy-duty one. If you need complex timing functions, however (and for autosave it doesn’t seem like you do) you might want to check out the Processing libraries page for Timing Utilities and CountdownTimer.

I am using multiple threads.

This is my save function:

void saveSelected(File selection) {
  // projektdatei speichern
  if (selection == null) { // wenn kein pfad gewählt
  } else {
    export_running_panel.setVisible(true); // zeige, dass beschäftigt
    String[] alldata = new String[var.data.size() + 5]; // neuer string, so lang wie die daten sind plus iso, stĂĽtz und zoom

    alldata[0] = "" + var.X_MIN + "/" + var.Y_MIN + "/" + var.X_MAX + "/" + var.Y_MAX + "/"; // fenstergröße
    for (int i = 0; i < var.iso_line_value.length; i++) { // fĂĽr jede iso linie
      alldata[0] += var.iso_line_value[i] + "/"; // wert der iso linie
    }

    alldata[1] = "";
    for (int i = 0; i < var.supports_xparallel.length; i++) { // fĂĽr jede zur x achse parallelen stĂĽtzachse
      alldata[1] += var.supports_xparallel[i] + "/"; // wert der stĂĽtzachse
    }

    alldata[2] = "";
    for (int i = 0; i < var.supports_yparallel.length; i++) { // fĂĽr jede zur y achse parallelen stĂĽtzachse
      alldata[2] += var.supports_yparallel[i] + "/"; // wert der stĂĽtzachse
    }

    alldata[3] = "";
    for (int i = 0; i < var.iso_line_points.length; i++) { // fĂĽr jede iso linie
      for (int j = 0; j < var.iso_line_points[i].length; j++) { // fĂĽr jeden knickpunkt
        alldata[3] += var.iso_line_points[i][j][0] + "/" + var.iso_line_points[i][j][1] + "/"; // x und y wert des knickpunktes
      }
    }

    if (var.EXTENDED_MODE) { // wenn der erweiterte isolinien modus aktiv ist
      alldata[3] += "true" + "/"; // true in die datei schreiben
    } else { // wenn der erweiterte isolinien modus noch nicht aktiviert wurde
      alldata[3] += "false" + "/"; // true in die datei schreiben
    }

    alldata[4] = "" + var.nvpoints.length + "/"; // anzahl der ungeeignet punkte in fĂĽnfte zeile
    for (int i = 0; i < var.nvpoints.length; i++) { // fĂĽr jeden punkt
      alldata[4] += var.nvpoints[i][0] + "/" + var.nvpoints[i][1] + "/"; // d und L wert dazu
    }

    for (int i = 5; i < var.data.size() + 5; i++) { // fĂĽr jeden datensatz
      alldata[i] = var.data.get(i-5).getStream(); // gesamter datensatz
    }

    String path = selection.getAbsolutePath();

    if (!path.substring(path.length()-4).equals(".txt")) { // wenn die endung nicht schon .txt ist
      path += ".txt"; // endung hinzufĂĽgen
    }

    saveStrings(path, alldata); // speichern als .txt datei
    
    File f = new File(path);
    surface.setTitle("" + f.getName() + "                    Table Tool : Version " + VERSIONNR);

    System.gc(); // datenmĂĽll vermeiden
    export_running_panel.setVisible(false); // nichtmehr beschäftigt
    save_for_next_start[0] = path; // datei beim nächsten öffnen laden
  }
}

I didn’t bother to take out the german comments. Just ignore them.
What that code does is to create a string array with a fixed length plus one element for each data-set. In the fixed part I store some setup data. Each data-set contains of multiple data points.

There are about 100 000 data points in total to store, depending on the measurements done before.

I’m not sure if I can just put the save at the end of the main loop since other threads might write to the variables?

EDIT:
This just came to my mind: What if

never is detected because my draw loop takes more than 1ms to run (I bet it does)?

Gah. Sorry, my mistake – my example is bad and will most certainly not be detected, because millis() will almost never be exactly – when checked, it will be slightly less or more. I will provide a (again, very single-threaded) example correction.

Kids: don’t post code without running it. After I’ve explained “don’t use == on millis()” many times on the forum, I went and did it anyway.

…okay, Simple example posted in the comment above, replacing the bad code. It stores the next save time in a global variable, and increments it forward whenever save is triggered by millis() being greater than the next scheduled time. For many timers you could wrap this in a simple timer class – if you don’t mind being dependent on the main thread timer, of course.

2 Likes