
  * Copyright ( C ) 2006 - 2009 Citrix Systems Inc. 
  * 
  * This program is free software ; you can redistribute it and/or modify 
  * it under the terms of the GNU Lesser General Public License as published 
  * by the Free Software Foundation ; version 2.1 only . with the special 
  * exception on linking described in file LICENSE . 
  * 
  * This program is distributed in the hope that it will be useful , 
  * but WITHOUT ANY WARRANTY ; without even the implied warranty of 
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE .   See the 
  * GNU Lesser General Public License for more details . 
 
 * Copyright (C) 2006-2009 Citrix Systems Inc.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation; version 2.1 only. with the special
 * exception on linking described in file LICENSE.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *)
module D = Debug.Make (struct let name = "xapi" end)

module R = Debug.Make (struct let name = "redo_log" end)

open D

let get_dbs_and_gen_counts () =
  List.map
    (fun conn -> (Parse_db_conf.generation_read conn, conn))
    (Db_conn_store.read_db_connections ())

* Returns true if the supplied connection actually exists , false otherwise . 
    	 Note that , although the two files should be present ( or absent ) together , 
    	 after upgrade we only have a database . In this case the generation 
    	 defaults back to 0L
    	Note that, although the two files should be present (or absent) together,
    	after upgrade we only have a database. In this case the generation
    	defaults back to 0L *)
let exists connection = Sys.file_exists connection.Parse_db_conf.path

(* This returns the most recent of the db connections to populate from. It also initialises the in-memory
   generation count to the largest of the db connections' generation counts *)
let choose connections =
  match List.filter exists connections with
  | [] ->
      None
  | c :: cs as connections ->
      List.iter
        (fun c ->
          debug "Dbconf contains: %s (generation %Ld)" c.Parse_db_conf.path
            (Parse_db_conf.generation_read c)
        )
        connections ;
      let gen, most_recent =
        List.fold_left
          (fun (g, c) c' ->
            let g' = Parse_db_conf.generation_read c' in
            if g' > g then (g', c') else (g, c)
          )
          (Parse_db_conf.generation_read c, c)
          cs
      in
      debug "Most recent db is %s (generation %Ld)"
        most_recent.Parse_db_conf.path gen ;
      Some most_recent

let preferred_write_db () = List.hd (Db_conn_store.read_db_connections ())

(* !!! FIX ME *)

(* This is set by signal handlers. It instructs the db thread to call exit after the next flush *)
let exit_on_next_flush = ref false

(* db flushing thread refcount: the last thread out of the door does the exit(0) when flush_on_exit is true *)
let with_lock = Xapi_stdext_threads.Threadext.Mutex.execute

let db_flush_thread_refcount_m = Mutex.create ()

let db_flush_thread_refcount = ref 0

let inc_db_flush_thread_refcount () =
  with_lock db_flush_thread_refcount_m (fun () ->
      db_flush_thread_refcount := !db_flush_thread_refcount + 1
  )

let dec_and_read_db_flush_thread_refcount () =
  with_lock db_flush_thread_refcount_m (fun () ->
      db_flush_thread_refcount := !db_flush_thread_refcount - 1 ;
      !db_flush_thread_refcount
  )

let pre_exit_hook () =
  (* We're about to exit. Close the active redo logs. *)
  Redo_log.with_active_redo_logs Redo_log.shutdown ;
  R.debug "Closed all active redo logs."

(* The connection flushing calls each lock the connection they're flushing to.
   The backend flush calls have to do enough locking (i.e. with the db_lock) to ensure that they
   flush a consistent snapshot. Backends must also ensure that they do not hold the global db_lock
   whilst they are writing to non-local storage.
*)
let flush_dirty_and_maybe_exit dbconn exit_spec =
  Db_conn_store.with_db_conn_lock dbconn (fun () ->
      (* if we're being told to shutdown by signal handler then flush every connection
         	  - the rationale is that we're not sure which db connections will be available on next restart *)
      ( if !exit_on_next_flush then
          let (_ : bool) = Backend_xml.flush_dirty dbconn in
          let refcount = dec_and_read_db_flush_thread_refcount () in
          (* last flushing thread close the door on the way out.. *)
          if refcount = 0 then (
            debug "refcount is 0; exiting" ;
            pre_exit_hook () ;
            exit 0
          ) else
            debug "refcount is %d; not exiting" refcount
      ) ;
      let was_anything_flushed = Backend_xml.flush_dirty dbconn in
      (* exit if we've been told to by caller *)
      ( match exit_spec with
      | None ->
          ()
      | Some ret_code ->
          pre_exit_hook () ; exit ret_code
      ) ;
      was_anything_flushed
  )

let flush dbconn db =
  debug "About to flush database: %s" dbconn.Parse_db_conf.path ;
  Db_conn_store.with_db_conn_lock dbconn (fun () -> Backend_xml.flush dbconn db)

 This returns the most recent of the db connections to populate from. It also initialises the in-memory
   generation count to the largest of the db connections' generation counts 
 !!! FIX ME 
 This is set by signal handlers. It instructs the db thread to call exit after the next flush 
 db flushing thread refcount: the last thread out of the door does the exit(0) when flush_on_exit is true 
 We're about to exit. Close the active redo logs. 
 The connection flushing calls each lock the connection they're flushing to.
   The backend flush calls have to do enough locking (i.e. with the db_lock) to ensure that they
   flush a consistent snapshot. Backends must also ensure that they do not hold the global db_lock
   whilst they are writing to non-local storage.

 if we're being told to shutdown by signal handler then flush every connection
         	  - the rationale is that we're not sure which db connections will be available on next restart 
 last flushing thread close the door on the way out.. 
 exit if we've been told to by caller 

  * Copyright ( C ) 2006 - 2009 Citrix Systems Inc. 
  * 
  * This program is free software ; you can redistribute it and/or modify 
  * it under the terms of the GNU Lesser General Public License as published 
  * by the Free Software Foundation ; version 2.1 only . with the special 
  * exception on linking described in file LICENSE . 
  * 
  * This program is distributed in the hope that it will be useful , 
  * but WITHOUT ANY WARRANTY ; without even the implied warranty of 
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE .   See the 
  * GNU Lesser General Public License for more details . 
 
 * Copyright (C) 2006-2009 Citrix Systems Inc.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation; version 2.1 only. with the special
 * exception on linking described in file LICENSE.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *)
module D = Debug.Make (struct let name = "xapi" end)

module R = Debug.Make (struct let name = "redo_log" end)

open D

let get_dbs_and_gen_counts () =
  List.map
    (fun conn -> (Parse_db_conf.generation_read conn, conn))
    (Db_conn_store.read_db_connections ())

* Returns true if the supplied connection actually exists , false otherwise . 
    	 Note that , although the two files should be present ( or absent ) together , 
    	 after upgrade we only have a database . In this case the generation 
    	 defaults back to 0L
    	Note that, although the two files should be present (or absent) together,
    	after upgrade we only have a database. In this case the generation
    	defaults back to 0L *)
let exists connection = Sys.file_exists connection.Parse_db_conf.path

let choose connections =
  match List.filter exists connections with
  | [] ->
      None
  | c :: cs as connections ->
      List.iter
        (fun c ->
          debug "Dbconf contains: %s (generation %Ld)" c.Parse_db_conf.path
            (Parse_db_conf.generation_read c)
        )
        connections ;
      let gen, most_recent =
        List.fold_left
          (fun (g, c) c' ->
            let g' = Parse_db_conf.generation_read c' in
            if g' > g then (g', c') else (g, c)
          )
          (Parse_db_conf.generation_read c, c)
          cs
      in
      debug "Most recent db is %s (generation %Ld)"
        most_recent.Parse_db_conf.path gen ;
      Some most_recent

let preferred_write_db () = List.hd (Db_conn_store.read_db_connections ())


let exit_on_next_flush = ref false

let with_lock = Xapi_stdext_threads.Threadext.Mutex.execute

let db_flush_thread_refcount_m = Mutex.create ()

let db_flush_thread_refcount = ref 0

let inc_db_flush_thread_refcount () =
  with_lock db_flush_thread_refcount_m (fun () ->
      db_flush_thread_refcount := !db_flush_thread_refcount + 1
  )

let dec_and_read_db_flush_thread_refcount () =
  with_lock db_flush_thread_refcount_m (fun () ->
      db_flush_thread_refcount := !db_flush_thread_refcount - 1 ;
      !db_flush_thread_refcount
  )

let pre_exit_hook () =
  Redo_log.with_active_redo_logs Redo_log.shutdown ;
  R.debug "Closed all active redo logs."

let flush_dirty_and_maybe_exit dbconn exit_spec =
  Db_conn_store.with_db_conn_lock dbconn (fun () ->
      ( if !exit_on_next_flush then
          let (_ : bool) = Backend_xml.flush_dirty dbconn in
          let refcount = dec_and_read_db_flush_thread_refcount () in
          if refcount = 0 then (
            debug "refcount is 0; exiting" ;
            pre_exit_hook () ;
            exit 0
          ) else
            debug "refcount is %d; not exiting" refcount
      ) ;
      let was_anything_flushed = Backend_xml.flush_dirty dbconn in
      ( match exit_spec with
      | None ->
          ()
      | Some ret_code ->
          pre_exit_hook () ; exit ret_code
      ) ;
      was_anything_flushed
  )

let flush dbconn db =
  debug "About to flush database: %s" dbconn.Parse_db_conf.path ;
  Db_conn_store.with_db_conn_lock dbconn (fun () -> Backend_xml.flush dbconn db)
