Cascaded Initialisation

From ByteWiki

Contents

Introduction

Straight from Zooba's Clog

"There is (at least) one common coding pattern which benefits greatly from the use of GOTO. I’ve

titled it, Cascaded Initialisation.

Take, for example, a function which intialises a system. There are 2 parts of this system and part A

must be initialised first. If part B fails to initialise then part A must be uninitialised."

Points to consider

  • What happens on exception on init?

Zooba - Return value is false and you must uninitialise everything so far

  • What happens on exception on uninit

Zooba - Assume the OS will handle it

  • Why am I spending too much time on this?

Solutions

Zooba's solution

  1. . ret = init_A();
  2. if(!ret) goto uninit_none;
  3. ret = init_B();
  4. if(!ret) goto uninit_A;
  5. ret = init_C();
  6. if(!ret) goto uninit_B;
  7. ret = init_D();
  8. if(!ret) goto uninit_C;
  9. return(SUCCESS);
  10. uninit_C:
  11. uninit_C();
  12. uninit_B:
  13. uninit_B();
  14. uninit_A:
  15. uninit_A();
  16. uninit_none:
  17. return(FAILED);

This is used frequently in code which is careful about resource usage. The Linux kernel and samba certainly do this all the time. Jnewbigin

Xavier's solution

i = 0
objects = [a, b, c, d]
success = true
 
# Init objects
for i in (0..objects.length-1)
  ret = objects[i].init
  if !ret
    success = false
    break
  end
end
 
# Uninit objects if anything failed
if !success && i > 0
  for j in (i-1..0)
    objects[i].uninit
  end
end
 
return success

pimaster's .NET solution

Sorry to take up so much space, but this is really a problem that could use a slightly more heavy weight approach.

  1. namespace Initialisable
  2. {
  3. /// <summary>
  4. /// The interface for an object that starts the work.
  5. /// </summary>
  6. public interface IInitialiser
  7. {
  8. void Add(IInitialisable thing);
  9. bool Initialise();
  10. }
  11. }
  1. namespace Initialisable
  2. {
  3. /// <summary>
  4. /// An Object that can be started and rolledback
  5. /// </summary>
  6. public interface IInitialisable
  7. {
  8. bool Initialise();
  9. void Rollback();
  10.  
  11. }
  12. }
  1. using System;
  2. namespace Initialisable
  3. {
  4. /// <summary>
  5. /// Single Threaded Initialiser
  6. /// </summary>
  7. public class PlainInitialiser : IInitialiser
  8. {
  9. private bool _Initialised = false; // don't init twice?
  10. private System.Collections.ArrayList _Items = new System.Collections.ArrayList();
  11.  
  12. public PlainInitialiser()
  13. {
  14. }
  15.  
  16. public void Add(IInitialisable thing)
  17. {
  18. if(! _Initialised )
  19. {
  20. _Items.Add(thing);
  21. }
  22. else
  23. {
  24. throw new InvalidOperationException("Can't add items to an initialised
  25. Initialiser");
  26. }
  27. }
  28.  
  29. /// <summary>
  30. /// Start the chain reaction
  31. /// </summary>
  32. /// <returns>True if everything started ok</returns>
  33. public bool Initialise()
  34. {
  35. bool result = false;
  36. if( ! _Initialised )
  37. {
  38. int i = 0;
  39. bool initOK = true;
  40. try
  41. {
  42. while( initOK && i < _Items.Count )
  43. {
  44. IInitialisable initer = _Items[i] as IInitialisable;
  45. try
  46. {
  47. initOK = initer.Initialise();
  48. }
  49. catch
  50. {
  51. initOK = false;
  52. throw;
  53. }
  54. if( initOK )
  55. {
  56. i++;
  57. }
  58. }
  59. result = initOK;
  60. }
  61. finally
  62. {
  63. while( ! initOK && i >= 0 )
  64. {
  65. try
  66. {
  67. IInitialisable initer = _Items[i] as
  68.  
  69. IInitialisable;
  70. initer.Rollback();
  71. }
  72. catch
  73. {
  74. // Implies all exceptions are ignored.
  75. }
  76. i--;
  77. }
  78. }
  79. }
  80. return result;
  81. }
  82. }
  83. }
  1. using System;
  2.  
  3. namespace Initialisable.Test
  4. {
  5. /// <summary>
  6. /// Summary description for OKInitialiser.
  7. /// </summary>
  8. public class OKInitialiser : IInitialisable
  9. {
  10. private string _Name;
  11. public OKInitialiser(string name)
  12. {
  13. _Name = name;
  14. }
  15. #region IInitialisable Members
  16. public bool Initialise()
  17. {
  18. try
  19. {
  20. System.Threading.Thread.Sleep(500);
  21. }
  22. catch(System.Threading.ThreadInterruptedException)
  23. {} // who cares
  24.  
  25. Console.WriteLine("Started {0}", _Name);
  26.  
  27. return true;
  28. }
  29. public void Rollback()
  30. {
  31. Console.WriteLine("Rollback {0}", _Name);
  32. }
  33. #endregion
  34. }
  35. }
  1. using System;
  2.  
  3. namespace Initialisable.Test
  4. {
  5. /// <summary>
  6. /// Summary description for FailInitialiser.
  7. /// </summary>
  8. public class FailInitialiser :IInitialisable
  9. {
  10. private string _Name;
  11. public FailInitialiser(string name)
  12. {
  13. _Name = name;
  14. }
  15. #region IInitialisable Members
  16. public bool Initialise()
  17. {
  18. try
  19. {
  20. System.Threading.Thread.Sleep(500);
  21. }
  22. catch(System.Threading.ThreadInterruptedException)
  23. {} // who cares
  24.  
  25. Console.WriteLine("Started failure for {0}", _Name);
  26. return false;
  27. }
  28. public void Rollback()
  29. {
  30. Console.WriteLine("Rollback failure for {0}", _Name);
  31. }
  32. #endregion
  33. }
  34. }
  1. using System;
  2.  
  3. namespace Initialisable.Test
  4. {
  5. /// <summary>
  6. /// Summary description for ExceptionInitialiser.
  7. /// </summary>
  8. public class ExceptionInitialiser  : IInitialisable
  9. {
  10. private string _Name;
  11. public ExceptionInitialiser(string name)
  12. {
  13. _Name = name;
  14. }
  15. #region IInitialisable Members
  16. public bool Initialise()
  17. {
  18. try
  19. {
  20. System.Threading.Thread.Sleep(500);
  21. }
  22. catch(System.Threading.ThreadInterruptedException)
  23. {} // who cares
  24.  
  25. Console.WriteLine("Throwing Exception for {0}", _Name);
  26.  
  27. throw new Exception("I am an unexpected random exception");
  28. }
  29. public void Rollback()
  30. {
  31. Console.WriteLine("Rollback Exception for {0}", _Name);
  32. }
  33. #endregion
  34. }
  35. }
  1. using System;
  2.  
  3. namespace Initialisable.Test
  4. {
  5. public delegate bool Init();
  6. public delegate void UnInit();
  7.  
  8. /// <summary>
  9. /// Might as well use some sytax sugar if putting on a demo.
  10. /// </summary>
  11. public class DelegatedInitialiser : IInitialisable
  12. {
  13. private Init _Init;
  14. private UnInit _UnInit;
  15. public DelegatedInitialiser(Init init, UnInit uninit)
  16. {
  17. _Init = init;
  18. _UnInit = uninit;
  19. }
  20. #region IInitialisable Members
  21. public bool Initialise()
  22. {
  23. return _Init();
  24. }
  25. public void Rollback()
  26. {
  27. _UnInit();
  28. }
  29. #endregion
  30. }
  31. }
  1. using System;
  2.  
  3. namespace ConsoleApplication1
  4. {
  5. /// <summary>
  6. /// And of course the test, which isn't in Nunit because I don't think I have it installed
  7. /// </summary>
  8. class InitTest
  9. {
  10. /// <summary>
  11. /// The main entry point for the application.
  12. /// </summary>
  13. [STAThread]
  14. static void Main(string[] args)
  15. {
  16. InitTest i = new InitTest();
  17. try
  18. {
  19. i.OKTest();
  20. i.FailTest();
  21. i.ExceptionTest();
  22. }
  23. catch
  24. {
  25. Console.WriteLine("Bad luck really?");
  26. }
  27. }
  28.  
  29. private void OKTest()
  30. {
  31. Initialisable.IInitialiser initer = new Initialisable.PlainInitialiser();
  32. initer.Add(new Initialisable.Test.OKInitialiser("I am thing 1"));
  33. initer.Add(new Initialisable.Test.OKInitialiser("I am thing 2"));
  34. initer.Add(new Initialisable.Test.OKInitialiser("I am thing 3"));
  35. initer.Add(new Initialisable.Test.OKInitialiser("I am thing 4"));
  36.  
  37. bool result = initer.Initialise();
  38. Console.WriteLine("The test result was (0)", result);
  39.  
  40. }
  41.  
  42. private void FailTest()
  43. {
  44. Initialisable.IInitialiser initer = new Initialisable.PlainInitialiser();
  45. initer.Add(new Initialisable.Test.OKInitialiser("I am thing 1"));
  46. initer.Add(new Initialisable.Test.OKInitialiser("I am thing 2"));
  47. initer.Add(new Initialisable.Test.FailInitialiser("I am thing 3"));
  48. initer.Add(new Initialisable.Test.OKInitialiser("I am thing 4"));
  49.  
  50. bool result = initer.Initialise();
  51. Console.WriteLine("The test result was (0)", result);
  52. }
  53.  
  54. private void ExceptionTest()
  55. {
  56. Initialisable.IInitialiser initer = new Initialisable.PlainInitialiser();
  57. initer.Add(new Initialisable.Test.OKInitialiser("I am thing 1"));
  58. initer.Add(new Initialisable.Test.OKInitialiser("I am thing 2"));
  59. initer.Add(new Initialisable.Test.ExceptionInitialiser("I am thing 3"));
  60. initer.Add(new Initialisable.Test.OKInitialiser("I am thing 4"));
  61.  
  62. bool result = initer.Initialise();
  63. Console.WriteLine("The test result was (0)", result);
  64. }
  65. }
  66. }

pimaster's Java solution

Interfaces for main use

  1. package byteclub.initialiser;
  2.  
  3. public interface IInitialisable {
  4.  
  5. boolean initialise() throws Exception;
  6. void rollback();
  7. }
  1. package byteclub.initialiser;
  2.  
  3. public interface IInitialiser {
  4.  
  5. void add(IInitialisable thing);
  6. boolean initialise() throws Exception;
  7. }
  1. package byteclub.initialiser;
  2.  
  3. public class InitialiserException extends RuntimeException {
  4. public InitialiserException()
  5. {
  6. super();
  7. }
  8. public InitialiserException(String message)
  9. {
  10. super(message);
  11. }
  12. public InitialiserException(String message, Exception inner)
  13. {
  14. super(message, inner);
  15. }
  16. public InitialiserException(Exception inner)
  17. {
  18. super(inner);
  19. }
  20. }

Implementation of the initialiser

  1. package byteclub.initialiser.impl;
  2.  
  3. import java.util.ArrayList;
  4. import java.util.logging.Logger;
  5.  
  6. import byteclub.initialiser.IInitialisable;
  7. import byteclub.initialiser.IInitialiser;
  8. import byteclub.initialiser.InitialiserException;
  9.  
  10. public class PlainInitialiser implements IInitialiser {
  11.  
  12. private boolean initialised = false;
  13. private ArrayList items = new ArrayList();
  14. public void add(IInitialisable thing) {
  15.  
  16. if( ! initialised )
  17. {
  18. items.add(thing);
  19. }
  20. else
  21. {
  22. throw new InitialiserException("Cannot add new objects to a started Initialiser");
  23. }
  24. }
  25.  
  26. public boolean initialise() throws Exception {
  27. boolean result = false;
  28. if( ! initialised )
  29. {
  30. int i = 0;
  31. boolean initOK = true;
  32. try
  33. {
  34. while( initOK && i < items.size() )
  35. {
  36. initOK = false;
  37. initOK = initialise((IInitialisable)items.get(i));
  38. if( initOK )
  39. {
  40. i++;
  41. }
  42. }
  43. result = initOK;
  44. }
  45. finally
  46. {
  47. i--; // We do not roll back the one that failed.
  48. while( ! initOK && i >= 0 )
  49. {
  50. rollback((IInitialisable)items.get(i));
  51. i--;
  52. }
  53. }
  54. }
  55. return result;
  56. }
  57.  
  58. private boolean initialise(IInitialisable initialisable) throws Exception {
  59. Logger.getAnonymousLogger().finer("Doing step {" + initialisable + "}");
  60. return initialisable.initialise();
  61. }
  62.  
  63. private void rollback(IInitialisable initialisable) {
  64. Logger.getAnonymousLogger().fine("Rolling back step {" + initialisable + "}");
  65. try
  66. {
  67. initialisable.rollback();
  68. }
  69. catch( Exception e )
  70. {
  71. Logger.getAnonymousLogger().warning("Roll back failed on " + initialisable + " with error " + e.getMessage());
  72. }
  73. }
  74.  
  75. }

Something with methods to call

  1. package byteclub.initialiser.example;
  2.  
  3. import java.io.File;
  4.  
  5. public class WindowsMock {
  6. public static Object GetProcessHeap()
  7. {
  8. return "I am the Process Heap";
  9. }
  10. public static Object HeapAllocation(Object someObject, int someValue) throws Exception
  11. {
  12. RandomCrash();
  13. return someObject.getClass();
  14. }