3.6. Restartable reads

An interesting libjf feature is the "restartable read" concept: many times a program has to process some input files to produce output files. What happens when an error occur processing a specific input record? If the program crashed the first time, there's more than a chance it will crash twice or more when trying to process the "dirty record"... Take a look to this example program:

Example 3-5. restartable_reads.c

     1	#include <jf_file.h>
       
     2	int main()
     3	{
     4	        int rc, c;
     5	        jf_file_t jf;
       
     6	        rc = jf_file_open(&jf, NULL, "jf_tut_foo", "R", NULL);
     7	        if (JF_RC_OK != rc)
     8	                return 1;
       
     9	        if (jf_file_eof(&jf)) {
    10	                rc = jf_file_rewind(&jf);
    11	                if (JF_RC_OK != rc)
    12	                        return 1;
    13	        } /* if (jf_file_eof(&jf)) */
       
    14	        rc = jf_file_getc(&jf, &c);
    15	        if (JF_RC_OK != rc)
    16	                return 1;
       
    17	        printf("Read char '%c' (0x%x)\n", c, c);
       
    18	        rc = jf_file_commit(&jf);
    19	        if (JF_RC_OK != rc)
    20	                return 1;
       
    21	        rc = jf_file_close(&jf);
    22	        if (JF_RC_OK != rc)
    23	                return 1;
       
    24	        printf("Restartable reads program is OK!\n");
    25	        return 0;
    26	}
      

restartable_reads.c code explanation

Row 6

the special flag "R" is used at open time: as documented in "API reference guide", the stream is positioned at last committed position

Row 9

check is the file pointer is at "end of file" position

Row 10

move file pointer to first file position; jf_file_seek might be used instead of jf_file_rewind if you preferred

Row 14

fetch only one char from journaled file

3.6.1. Compilation and execution

You may use this command to compile restartable_reads.c source code:

libtool --mode=link gcc -Wall -I/opt/libjf/include -L/opt/libjf/lib -ljf \
        -o restartable_reads restartable_reads.c
      
restartable_reads program needs a not empty jf_tut_foo journaled file to be executed; our old friend hello_world can help us one more time:
tiian@linux:~/tutorial> ./hello_world
Hello world program is OK!
tiian@linux:~/tutorial> ./restartable_reads
Read char 'H' (0x48)
Restartable reads program is OK!
      
restartable_reads program fetched "H", the first char in journaled file jf_tut_foo. What happens if we run restartable_reads again?
tiian@linux:~/tutorial> ./restartable_reads
Read char 'e' (0x65)
Restartable reads program is OK!
      
"e", the second char in journaled file jf_tut_foo is fetched... Are you guessing what will happen at next execution?
tiian@linux:~/tutorial> ./restartable_reads
Read char 'l' (0x6c)
Restartable reads program is OK!
      
"l", the third char in journaled file is fetched!

The same behavior will happen when using a different read method like jf_file_gets or jf_file_read: at commit time, the file pointer is moved and transactionally kept by journal.

3.6.2. Restartable reads and rollback

What happens when a restartable read transaction is backed out by an explicit or implicit rollback? Take a look to this example:

Example 3-6. restartable_reads_rollback.c

     1	#include <jf_file.h>
       
     2	int main()
     3	{
     4	        int rc, c;
     5	        jf_file_t jf;
       
     6	        rc = jf_file_open(&jf, NULL, "jf_tut_foo", "R", NULL);
     7	        if (JF_RC_OK != rc)
     8	                return 1;
       
     9	        if (jf_file_eof(&jf)) {
    10	                rc = jf_file_rewind(&jf);
    11	                if (JF_RC_OK != rc)
    12	                        return 1;
    13	        } /* if (jf_file_eof(&jf)) */
       
    14	        rc = jf_file_getc(&jf, &c);
    15	        if (JF_RC_OK != rc)
    16	                return 1;
       
    17	        printf("Read char '%c' (0x%x)\n", c, c);
       
    18	        rc = jf_file_rollback(&jf);
    19	        if (JF_RC_OK != rc)
    20	                return 1;
       
    21	        rc = jf_file_close(&jf);
    22	        if (JF_RC_OK != rc)
    23	                return 1;
       
    24	        printf("Restartable reads rollback program is OK!\n");
    25	        return 0;
    26	}
        

The only difference between restartable_reads.c and restartable_reads_rollback.c is at row 18 where jf_file_commit has been replaced with jf_file_rollback.

Compile and run this example:

libtool --mode=link gcc -Wall -I/opt/libjf/include -L/opt/libjf/lib -ljf \
        -o restartable_reads_rollback restartable_reads_rollback.c
tiian@linux:~/tutorial> ./hello_world
Hello world program is OK!
tiian@linux:~/tutorial> ./restartable_reads_rollback
Read char 'H' (0x48)
Restartable reads rollback program is OK!
tiian@linux:~/tutorial> ./restartable_reads_rollback
Read char 'H' (0x48)
Restartable reads rollback program is OK!
tiian@linux:~/tutorial> ./restartable_reads_rollback
Read char 'H' (0x48)
Restartable reads rollback program is OK!
      
Every execution reads the first char because the read transaction is not committed. Now you can play some minutes with "restartable_reads" and "restartable_reads_rollback" to simulate committed and backed out transactions.

3.6.3. Conclusions

Using libjf you are able to write restartable applications: