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
the special flag "R" is used at open time: as documented in "API reference guide", the stream is positioned at last committed position
check is the file pointer is at "end of file" position
move file pointer to first file position;
jf_file_seek
might be used instead of
jf_file_rewind
if you preferred
fetch only one char from journaled file
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.crestartable_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.
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.
Using libjf you are able to write restartable applications:
you don't have to deal with saving in some "safe place" the last read record to avoid multiple processing
using partial transactions, you can commit a read before the global transaction commit and avoid a crash generated by dirty input at next restart