From 77b17de987103e0860658ad44654bf48ac6950b7 Mon Sep 17 00:00:00 2001 From: skidder Date: Mon, 8 Sep 2003 20:23:46 +0000 Subject: [PATCH] NBACKUP-related changes and some clean-up --- builds/posix/Makefile.in.boot.gpre | 4 +- builds/posix/Makefile.in.client.util | 14 +- builds/posix/Makefile.in.embed.util | 14 +- builds/posix/make.defaults | 3 +- builds/posix/make.shared.variables | 4 +- builds/posix/prefix.darwin | 6 +- builds/posix/prefix.example | 4 +- builds/posix/prefix.freebsd | 6 +- builds/posix/prefix.linux | 12 +- builds/posix/prefix.mingw | 4 +- builds/posix/prefix.sinixz | 6 +- builds/posix/prefix.solaris | 4 +- builds/posix/prefix.solx86 | 6 +- builds/posix/prefix.solx86gcc | 6 +- configure.in | 4 +- src/common/classes/alloc.cpp | 41 +- src/common/classes/alloc.h | 47 +- src/common/classes/class_perf.cpp | 41 +- src/common/classes/class_test.cpp | 41 +- src/common/classes/locks.h | 41 +- src/common/classes/rwlock.h | 41 +- src/common/classes/tree.h | 41 +- src/common/classes/vector.h | 41 +- src/dsql/parse.y | 2 +- src/include/firebird.h | 6 +- src/jrd/cch.cpp | 411 +++++-- src/jrd/cch.h | 18 +- src/jrd/cch_proto.h | 1 + src/jrd/common.h | 63 +- src/jrd/dfw.epp | 10 +- src/jrd/err.cpp | 5 - src/jrd/err_proto.h | 16 - src/jrd/event.cpp | 17 +- src/jrd/gds.cpp | 117 +- src/jrd/gds_proto.h | 2 + src/jrd/isc_i_proto.h | 69 -- src/jrd/isc_ipc.cpp | 1155 ------------------- src/jrd/isc_s_proto.h | 6 +- src/jrd/isc_sync.cpp | 83 +- src/jrd/lck.cpp | 8 + src/jrd/lck.h | 5 +- src/jrd/lck_proto.h | 2 + src/jrd/nbak.cpp | 1543 +++++++++++++++----------- src/jrd/nbak.h | 166 ++- src/jrd/os/guid.h | 41 +- src/jrd/os/posix/guid.cpp | 3 +- src/jrd/os/posix/isc_ipc.cpp | 583 ++++++++++ src/jrd/os/posix/unix.cpp | 2 +- src/jrd/os/win32/guid.cpp | 41 +- src/jrd/os/win32/isc_ipc.cpp | 302 +++++ src/jrd/svc.cpp | 19 +- src/jrd/thd.h | 9 +- src/jrd/why.cpp | 4 +- src/lock/lock.cpp | 41 +- src/lock/lock_proto.h | 2 + src/lock/print.cpp | 2 +- src/remote/inet.cpp | 33 +- src/remote/inet_server.cpp | 44 +- src/remote/os/win32/srvr_w32.cpp | 2 +- src/remote/server.cpp | 2 +- src/utilities/gstat/ppg.cpp | 30 +- src/utilities/nbackup.cpp | 76 +- src/wal/wal.cpp | 16 +- src/wal/walc.cpp | 6 +- src/wal/walc_proto.h | 2 +- src/wal/walw.cpp | 5 +- 66 files changed, 2935 insertions(+), 2466 deletions(-) delete mode 100644 src/jrd/isc_i_proto.h delete mode 100644 src/jrd/isc_ipc.cpp create mode 100644 src/jrd/os/posix/isc_ipc.cpp create mode 100644 src/jrd/os/win32/isc_ipc.cpp diff --git a/builds/posix/Makefile.in.boot.gpre b/builds/posix/Makefile.in.boot.gpre index 0f7cd0af4a..81b9b91d18 100644 --- a/builds/posix/Makefile.in.boot.gpre +++ b/builds/posix/Makefile.in.boot.gpre @@ -27,7 +27,7 @@ # Contributor(s): # # -# $Id: Makefile.in.boot.gpre,v 1.14 2003-05-25 10:44:57 fsg Exp $ +# $Id: Makefile.in.boot.gpre,v 1.15 2003-09-08 20:23:31 skidder Exp $ # ROOT=.. ObjModuleName=boot.gpre @@ -41,7 +41,7 @@ include $(ROOT)/gen/make.shared.variables @SET_MAKE@ -JRDBOOT_Files = dsc.cpp gds.cpp isc_ipc.cpp isc.cpp dls.cpp $(JRDBOOT_Extra_Files) +JRDBOOT_Files = dsc.cpp gds.cpp isc.cpp dls.cpp $(JRDBOOT_Extra_Files) JRDBOOT_Sources = $(addprefix jrd/, $(JRDBOOT_Files)) JRDBOOT_Objects = $(addprefix $(OBJ)/, $(addsuffix .o, $(basename $(JRDBOOT_Sources)))) diff --git a/builds/posix/Makefile.in.client.util b/builds/posix/Makefile.in.client.util index 027c9b936a..b747c0bf7b 100644 --- a/builds/posix/Makefile.in.client.util +++ b/builds/posix/Makefile.in.client.util @@ -27,7 +27,7 @@ # Contributor(s): # # -# $Id: Makefile.in.client.util,v 1.12 2003-08-06 16:30:33 skidder Exp $ +# $Id: Makefile.in.client.util,v 1.13 2003-09-08 20:23:31 skidder Exp $ # ROOT=.. ObjModuleName=client.util @@ -41,6 +41,11 @@ include $(ROOT)/gen/make.shared.variables @SET_MAKE@ +LOCKPRINT_Files= print.cpp +LOCKPRINT_Sources = $(addprefix lock/, $(LOCKPRINT_Files)) +LOCKPRINT_Objects = $(addprefix $(OBJ)/, $(addsuffix .o, $(basename $(LOCKPRINT_Sources)))) + + NBACKUP_Sources= utilities/nbackup.cpp jrd/db_alias.cpp NBACKUP_Objects = $(addprefix $(OBJ)/, $(addsuffix .o, $(basename $(NBACKUP_Sources)))) @@ -104,7 +109,7 @@ AllObjects = $(CREATEDB_Object) $(DROP_Object) \ Dependencies = $(AllObjects:.o=.d) -.PHONY: all create_db gstat gds_drop gds_relay gsec fbguard fbmgr_bin nbackup +.PHONY: all create_db gstat gds_drop gds_relay gsec fbguard fbmgr_bin nbackup fb_lock_print all: $(CLIENT_UTIL_TARGETS) @@ -119,6 +124,11 @@ nbackup: $(LIBFBCLIENT_SO) $(NBACKUP) $(NBACKUP): $(NBACKUP_Objects) $(LD) $(LINK_OPTS) $(NBACKUP_Objects) -o $@ -L$(LIB) -lfbclient $(LINK_LIBS) +fb_lock_print: $(LIBFBCLIENT_SO) $(LOCKPRINT) + +$(LOCKPRINT): $(LOCKPRINT_Objects) + $(LD) $(LINK_OPTS) $(LOCKPRINT_Objects) -o $@ -L$(LIB) -lfbclient $(LINK_LIBS) + gstat : $(LIBFBCLIENT_SO) $(GSTAT) diff --git a/builds/posix/Makefile.in.embed.util b/builds/posix/Makefile.in.embed.util index f34a5eba1f..ffa3cc0e03 100644 --- a/builds/posix/Makefile.in.embed.util +++ b/builds/posix/Makefile.in.embed.util @@ -27,7 +27,7 @@ # Contributor(s): # # -# $Id: Makefile.in.embed.util,v 1.10 2003-08-06 16:30:34 skidder Exp $ +# $Id: Makefile.in.embed.util,v 1.11 2003-09-08 20:23:31 skidder Exp $ # ROOT=.. ObjModuleName=embed.util @@ -41,6 +41,11 @@ include $(ROOT)/gen/make.shared.variables @SET_MAKE@ +LOCKPRINT_Files= print.cpp +LOCKPRINT_Sources = $(addprefix lock/, $(LOCKPRINT_Files)) +LOCKPRINT_Objects = $(addprefix $(OBJ)/, $(addsuffix .o, $(basename $(LOCKPRINT_Sources)))) + + NBACKUP_Files= nbackup.cpp NBACKUP_Sources = $(addprefix utilities/, $(NBACKUP_Files)) NBACKUP_Objects = $(addprefix $(OBJ)/, $(addsuffix .o, $(basename $(NBACKUP_Sources)))) @@ -95,7 +100,7 @@ AllObjects = $(CREATEDB_Objects) $(DROP_Objects) \ Dependencies = $(AllObjects:.o=.d) -.PHONY: create_db gstat gds_drop gds_relay gsec ibguard ibmgr_bin nbackup +.PHONY: create_db gstat gds_drop gds_relay gsec ibguard ibmgr_bin nbackup fb_lock_print all: $(EMBED_UTIL_TARGETS) @@ -115,6 +120,11 @@ nbackup: $(LIBFBEMBED_SO) $(NBACKUP) $(NBACKUP): $(NBACKUP_Objects) $(LD) $(LINK_OPTS) $^ -o $@ -L$(LIB) -lfbembed $(LINK_LIBS) +fb_lock_print: $(LIBFBEMBED_SO) $(LOCKPRINT) + +$(LOCKPRINT): $(LOCKPRINT_Objects) + $(LD) $(LINK_OPTS) $^ -o $@ -L$(LIB) -lfbembed $(LINK_LIBS) + gstat : $(LIBFBEMBED_SO) $(GSTAT) diff --git a/builds/posix/make.defaults b/builds/posix/make.defaults index 0aecb7ba28..69675df152 100755 --- a/builds/posix/make.defaults +++ b/builds/posix/make.defaults @@ -26,7 +26,7 @@ # Contributor(s): # # -# $Id: make.defaults,v 1.29 2003-09-01 14:22:50 alexpeshkoff Exp $ +# $Id: make.defaults,v 1.30 2003-09-08 20:23:31 skidder Exp $ # @@ -215,6 +215,7 @@ CREATE_DB = $(BIN)/create_db$(EXEC_EXT) GDS_DROP = $(BIN)/gds_drop$(EXEC_EXT) GSTAT = $(BIN)/gstat$(EXEC_EXT) NBACKUP = $(BIN)/nbackup$(EXEC_EXT) +LOCKPRINT = $(BIN)/fb_lock_print$(EXEC_EXT) GSEC = $(BIN)/gsec$(EXEC_EXT) GFIX = $(BIN)/gfix$(EXEC_EXT) GDS_REBUILD = $(BIN)/gds_rebuild$(EXEC_EXT) diff --git a/builds/posix/make.shared.variables b/builds/posix/make.shared.variables index 5e32c73d51..4b713c9858 100644 --- a/builds/posix/make.shared.variables +++ b/builds/posix/make.shared.variables @@ -17,7 +17,7 @@ WHY_Sources = why.cpp JRD_ClientFiles = alt.cpp cvt.cpp dsc.cpp dls.cpp \ - enc.cpp gds.cpp isc.cpp isc_file.cpp isc_ipc.cpp \ + enc.cpp gds.cpp isc.cpp isc_file.cpp \ isc_sync.cpp perf.cpp sch.cpp sdl.cpp status.cpp \ thd.cpp utl.cpp \ $(WHY_Sources) @@ -247,7 +247,7 @@ FBCOMMON_Objects = $(addprefix $(OBJ)/, $(addsuffix .o, $(basename $(FBCOMMON_So # Platform Manager # just in case if make.platform defined some files -OS_SPECIFIC_Files += config_root.cpp path_utils.cpp mod_loader.cpp fbsyslog.cpp guid.cpp +OS_SPECIFIC_Files += config_root.cpp path_utils.cpp mod_loader.cpp fbsyslog.cpp guid.cpp isc_ipc.cpp OS_SPECIFIC_Sources = $(addprefix $(PLATFORM_PATH)/, $(OS_SPECIFIC_Files)) OS_SPECIFIC_Objects = $(addprefix $(OBJ)/, $(addsuffix .o, $(basename $(OS_SPECIFIC_Sources)))) diff --git a/builds/posix/prefix.darwin b/builds/posix/prefix.darwin index 68778594f1..c0a42f0040 100644 --- a/builds/posix/prefix.darwin +++ b/builds/posix/prefix.darwin @@ -15,7 +15,7 @@ # All Rights Reserved. # Contributor(s): ______________________________________. # Start of file prefix.darwin: $(VERSION) @PLATFORM@ -#$Id: prefix.darwin,v 1.10 2003-08-06 16:30:35 skidder Exp $ +#$Id: prefix.darwin,v 1.11 2003-09-08 20:23:31 skidder Exp $ # 2 Oct 2002, Nickolay Samofatov - Major Cleanup OS_ServerFiles=inet_server.cpp @@ -25,7 +25,7 @@ DEV_FLAGS=-ggdb -DDARWIN -DDEBUG_GDS_ALLOC -pipe -MMD -p -fPIC -Wall LIB_LINK_OPTIONS:=-shared LIB_LINK_RPATH:=-Wl,-rpath, LIB_LINK_SONAME:=-Wl,-soname, -EMBED_UTIL_TARGETS=gstat gds_drop gds_relay gsec nbackup -CLIENT_UTIL_TARGETS=gds_drop gds_delay gstat gsec fbguard fbmgr_bin nbackup +EMBED_UTIL_TARGETS=gstat gds_drop gds_relay gsec nbackup fb_lock_print +CLIENT_UTIL_TARGETS=gds_drop gds_delay gstat gsec fbguard fbmgr_bin nbackup fb_lock_print Physical_IO_Module=os/posix/unix.cpp diff --git a/builds/posix/prefix.example b/builds/posix/prefix.example index 780adddfca..d52484be16 100644 --- a/builds/posix/prefix.example +++ b/builds/posix/prefix.example @@ -12,7 +12,7 @@ OS_ServerFiles=inet_server.cpp LIB_LINK_OPTIONS:= LIB_LINK_RPATH:=-Wl,-rpath, LIB_LINK_SONAME:=-Wl,-soname, -EMBED_UTIL_TARGETS=gstat gds_drop gds_relay gsec nbackup -CLIENT_UTIL_TARGETS=gds_drop gds_delay gstat gsec fbguard fbmgr_bin nbackup +EMBED_UTIL_TARGETS=gstat gds_drop gds_relay gsec nbackup fb_lock_print +CLIENT_UTIL_TARGETS=gds_drop gds_delay gstat gsec fbguard fbmgr_bin nbackup fb_lock_print Physical_IO_Module=os/posix/unix.cpp \ No newline at end of file diff --git a/builds/posix/prefix.freebsd b/builds/posix/prefix.freebsd index 35d9047d39..3e1af70991 100644 --- a/builds/posix/prefix.freebsd +++ b/builds/posix/prefix.freebsd @@ -15,7 +15,7 @@ # All Rights Reserved. # Contributor(s): ______________________________________. # Start of file prefix.freebsd: $(VERSION) @PLATFORM@ -#$Id: prefix.freebsd,v 1.8 2003-08-06 16:30:36 skidder Exp $ +#$Id: prefix.freebsd,v 1.9 2003-09-08 20:23:31 skidder Exp $ # 2 Oct 2002, Nickolay Samofatov - Major Cleanup OS_ServerFiles=inet_server.cpp @@ -25,7 +25,7 @@ DEV_FLAGS=-ggdb -DFREEBSD -DDEBUG_GDS_ALLOC -pipe -MMD -p -fPIC -Wall LIB_LINK_OPTIONS:=-shared LIB_LINK_RPATH:=-Wl,-rpath, LIB_LINK_SONAME:=-Wl,-soname, -EMBED_UTIL_TARGETS=gstat gds_drop gds_relay gsec nbackup -CLIENT_UTIL_TARGETS=gds_drop gds_delay gstat gsec fbguard fbmgr_bin nbackup +EMBED_UTIL_TARGETS=gstat gds_drop gds_relay gsec nbackup fb_lock_print +CLIENT_UTIL_TARGETS=gds_drop gds_delay gstat gsec fbguard fbmgr_bin nbackup fb_lock_print Physical_IO_Module=os/posix/unix.cpp \ No newline at end of file diff --git a/builds/posix/prefix.linux b/builds/posix/prefix.linux index 44f66cee52..65835ef3a7 100644 --- a/builds/posix/prefix.linux +++ b/builds/posix/prefix.linux @@ -15,20 +15,22 @@ # All Rights Reserved. # Contributor(s): ______________________________________. # Start of file prefix.linux: $(VERSION) $(PLATFORM) -#$Id: prefix.linux,v 1.22 2003-08-19 11:42:45 brodsom Exp $ +#$Id: prefix.linux,v 1.23 2003-09-08 20:23:31 skidder Exp $ # 2 Oct 2002, Nickolay Samofatov - Major cleanup # -fno-builtin is used because GCC 3.0-3.2.2 had bug with builtins expansion # you may remove it if engine is getting compiled with any other GCC version -PROD_FLAGS=-O3 -march=i586 -mcpu=i686 -fomit-frame-pointer -fno-builtin -DNDEBUG -DLINUX -Dlint -pipe -MMD -fPIC -fmessage-length=0 -DEV_FLAGS=-ggdb -DLINUX -DDEBUG_GDS_ALLOC -Dlint -pipe -MMD -p -fPIC -Wall -Wno-switch -Wno-parentheses -Wno-unknown-pragmas -Wno-unused-variable -fmessage-length=0 +PROD_FLAGS=-O3 -march=i586 -mcpu=i686 -fomit-frame-pointer -fno-builtin -DNDEBUG -DLINUX -pipe -MMD -fPIC -fmessage-length=0 +# uncomment if you need PROD_BUILD engine that is possible to debug +#PROD_FLAGS=-ggdb -O3 -march=i586 -mcpu=i686 -fno-builtin -DNDEBUG -DLINUX -pipe -MMD -fPIC -fmessage-length=0 +DEV_FLAGS=-ggdb -DLINUX -DDEBUG_GDS_ALLOC -pipe -MMD -p -fPIC -Wall -Wno-switch -Wno-parentheses -Wno-unknown-pragmas -Wno-unused-variable -fmessage-length=0 OS_ServerFiles=inet_server.cpp LIB_LINK_OPTIONS:=-shared LIB_LINK_RPATH:=-Wl,-rpath, LIB_LINK_SONAME:=-Wl,-soname, -EMBED_UTIL_TARGETS=gstat gds_drop gds_relay gsec nbackup -CLIENT_UTIL_TARGETS=gds_drop gds_relay gstat gsec fbguard fbmgr_bin nbackup +EMBED_UTIL_TARGETS=gstat gds_drop gds_relay gsec nbackup fb_lock_print +CLIENT_UTIL_TARGETS=gds_drop gds_relay gstat gsec fbguard fbmgr_bin nbackup fb_lock_print Physical_IO_Module=os/posix/unix.cpp diff --git a/builds/posix/prefix.mingw b/builds/posix/prefix.mingw index 5ef010b541..72c7e740e7 100644 --- a/builds/posix/prefix.mingw +++ b/builds/posix/prefix.mingw @@ -31,8 +31,8 @@ LIB_LINK_OPTIONS:=-shared -Wl,--add-stdcall-alias LIB_LINK_RPATH:=-Wl,-rpath, LIB_LINK_SONAME:=-Wl,-soname, LIB_GUI:= -mwindows -lcomctl32 -lgdi32 -EMBED_UTIL_TARGETS:=gstat gsec nbackup -CLIENT_UTIL_TARGETS:=gstat gsec ibguard instsvc instreg nbackup +EMBED_UTIL_TARGETS:=gstat gsec nbackup fb_lock_print +CLIENT_UTIL_TARGETS:=gstat gsec ibguard instsvc instreg nbackup fb_lock_print WIN_IPSERVER_Files:=ipserver.cpp alli.cpp Physical_IO_Module=os/win32/winnt.cpp diff --git a/builds/posix/prefix.sinixz b/builds/posix/prefix.sinixz index d9c7926a76..d22b5ef0eb 100644 --- a/builds/posix/prefix.sinixz +++ b/builds/posix/prefix.sinixz @@ -17,7 +17,7 @@ # # Erik Kunze, Philosys GmbH, # -# $Id: prefix.sinixz,v 1.18 2003-08-06 16:30:36 skidder Exp $ +# $Id: prefix.sinixz,v 1.19 2003-09-08 20:23:31 skidder Exp $ # # 2 Oct 2002, Nickolay Samofatov - Major Cleanup @@ -39,8 +39,8 @@ OS_ServerFiles=inet_server.cpp LIB_LINK_OPTIONS:=-shared LIB_LINK_RPATH:=-Wl,-rpath, LIB_LINK_SONAME:=-Wl,-soname, -EMBED_UTIL_TARGETS=gstat gds_drop gds_relay gsec nbackup -CLIENT_UTIL_TARGETS=gds_drop gds_delay gstat gsec fbguard fbmgr_bin nbackup +EMBED_UTIL_TARGETS=gstat gds_drop gds_relay gsec nbackup fb_lock_print +CLIENT_UTIL_TARGETS=gds_drop gds_delay gstat gsec fbguard fbmgr_bin nbackup fb_lock_print Physical_IO_Module=os/posix/unix.cpp diff --git a/builds/posix/prefix.solaris b/builds/posix/prefix.solaris index adc87c8b89..0b3def58b6 100644 --- a/builds/posix/prefix.solaris +++ b/builds/posix/prefix.solaris @@ -57,7 +57,7 @@ OS_ServerFiles=inet_server.cpp LIB_LINK_OPTIONS:= -G LIB_LINK_RPATH:=-R LIB_LINK_SONAME:=-h -EMBED_UTIL_TARGETS=gstat gds_drop gds_relay gsec nbackup -CLIENT_UTIL_TARGETS=gds_drop gds_delay gstat gsec fbguard fbmgr_bin nbackup +EMBED_UTIL_TARGETS=gstat gds_drop gds_relay gsec nbackup fb_lock_print +CLIENT_UTIL_TARGETS=gds_drop gds_delay gstat gsec fbguard fbmgr_bin nbackup fb_lock_print Physical_IO_Module=os/posix/unix.cpp diff --git a/builds/posix/prefix.solx86 b/builds/posix/prefix.solx86 index 36db293a63..c742c118e6 100644 --- a/builds/posix/prefix.solx86 +++ b/builds/posix/prefix.solx86 @@ -19,7 +19,7 @@ # # Use SOLX86 to identify x86 version of Solaris. Neil McCalden # -# $Id: prefix.solx86,v 1.21 2003-08-08 06:29:52 kkuznetsov Exp $ +# $Id: prefix.solx86,v 1.22 2003-09-08 20:23:31 skidder Exp $ # # Start of file prefix.solaris X 86 : $(VERSION) $(PLATFORM) # 2 Oct 2002, Nickolay Samofatov - Major Cleanup @@ -63,7 +63,7 @@ LIB_LINK_OPTIONS:= -G LIB_LINK_RPATH:=-R LIB_LINK_SONAME:=-h #LINK_OPTS:= $(LIB_LINK_RPATH)$(FirebirdInstallPrefix)/lib $(LIB_LINK_RPATH)$(FirebirdInstallPrefix)/intl -EMBED_UTIL_TARGETS=gstat gds_drop gds_relay gsec nbackup -CLIENT_UTIL_TARGETS=gds_drop gds_delay gstat gsec fbguard fbmgr_bin nbackup +EMBED_UTIL_TARGETS=gstat gds_drop gds_relay gsec nbackup fb_lock_print +CLIENT_UTIL_TARGETS=gds_drop gds_delay gstat gsec fbguard fbmgr_bin nbackup fb_lock_print Physical_IO_Module=os/posix/unix.cpp diff --git a/builds/posix/prefix.solx86gcc b/builds/posix/prefix.solx86gcc index ece5a96df6..91b8d41360 100644 --- a/builds/posix/prefix.solx86gcc +++ b/builds/posix/prefix.solx86gcc @@ -18,7 +18,7 @@ # # Use SOLX86 to identify x86 version of Solaris. Neil McCalden # -# $Id: prefix.solx86gcc,v 1.3 2003-08-06 16:30:36 skidder Exp $ +# $Id: prefix.solx86gcc,v 1.4 2003-09-08 20:23:31 skidder Exp $ # # Start of file prefix.solaris X 86 : $(VERSION) $(PLATFORM) # 2 Oct 2002, Nickolay Samofatov - Major Cleanup @@ -62,7 +62,7 @@ LIB_LINK_OPTIONS:= -G LIB_LINK_RPATH:=-R LIB_LINK_SONAME:=-h #LINK_OPTS:= $(LIB_LINK_RPATH)$(FirebirdInstallPrefix)/lib $(LIB_LINK_RPATH)$(FirebirdInstallPrefix)/intl -EMBED_UTIL_TARGETS=gstat gds_drop gds_relay gsec nbackup -CLIENT_UTIL_TARGETS=gds_drop gds_delay gstat gsec fbguard fbmgr_bin nbackup +EMBED_UTIL_TARGETS=gstat gds_drop gds_relay gsec nbackup fb_lock_print +CLIENT_UTIL_TARGETS=gds_drop gds_delay gstat gsec fbguard fbmgr_bin nbackup fb_lock_print Physical_IO_Module=os/posix/unix.cpp diff --git a/configure.in b/configure.in index b3c8e7747f..a2d3e3bd9c 100644 --- a/configure.in +++ b/configure.in @@ -1,4 +1,4 @@ -dnl $Id: configure.in,v 1.154 2003-08-22 10:26:06 aafemt Exp $ +dnl $Id: configure.in,v 1.155 2003-09-08 20:23:29 skidder Exp $ dnl ############################# INITIALISATION ############################### @@ -610,6 +610,7 @@ mkdir -p temp/client.util/utilities/guard mkdir -p temp/client.util/utilities/gsec mkdir -p temp/client.util/utilities/gstat mkdir -p temp/client.util/utilities/install +mkdir -p temp/client.util/lock mkdir -p temp/client.util/iscguard mkdir -p temp/client.util/remote/os/win32 mkdir -p temp/client.util/jrd/os/win32 @@ -640,6 +641,7 @@ mkdir -p temp/libfbstatic/common/config mkdir -p temp/embed.lockmgr/lock mkdir -p temp/embed.util/utilities/gstat mkdir -p temp/embed.util/utilities/gsec +mkdir -p temp/embed.util/lock mkdir -p temp/embed.gbak/burp mkdir -p temp/embed.gfix/alice mkdir -p temp/embed.isql/isql diff --git a/src/common/classes/alloc.cpp b/src/common/classes/alloc.cpp index 6369afb3ca..caeae974b6 100644 --- a/src/common/classes/alloc.cpp +++ b/src/common/classes/alloc.cpp @@ -3,24 +3,37 @@ * MODULE: alloc.cpp * DESCRIPTION: Memory Pool Manager (based on B+ tree) * - * The contents of this file are subject to the Interbase Public - * License Version 1.0 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy - * of the License at http://www.Inprise.com/IPL.html + * This library 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; either + * version 2.1 of the License, or (at your option) any later version. + * You may obtain a copy of the Licence at + * http://www.gnu.org/licences/lgpl.html + * + * As a special exception this file can also be included in modules + * with other source code as long as that source code has been + * released under an Open Source Initiative certificed licence. + * More information about OSI certification can be found at: + * http://www.opensource.org + * + * This module 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 Licence for more details. + * + * This module was created by members of the firebird development + * team. All individual contributions remain the Copyright (C) of + * those individuals and all rights are reserved. Contributors to + * this file are either listed below or can be obtained from a CVS + * history command. * - * Software distributed under the License is distributed on an - * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express - * or implied. See the License for the specific language governing - * rights and limitations under the License. + * Created by: Nickolay Samofatov * - * The Original Code was created by Inprise Corporation - * and its predecessors. Portions created by Inprise Corporation are - * Copyright (C) Inprise Corporation. + * Contributor(s): + * * - * Created by: Nickolay Samofatov + * $Id: alloc.cpp,v 1.32 2003-09-08 20:23:32 skidder Exp $ * - * All Rights Reserved. - * Contributor(s): ______________________________________. */ #include "../../include/firebird.h" diff --git a/src/common/classes/alloc.h b/src/common/classes/alloc.h index a88ac3b05b..110a6f9cfa 100644 --- a/src/common/classes/alloc.h +++ b/src/common/classes/alloc.h @@ -3,26 +3,39 @@ * MODULE: alloc.h * DESCRIPTION: Memory Pool Manager (based on B+ tree) * - * The contents of this file are subject to the Interbase Public - * License Version 1.0 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy - * of the License at http://www.Inprise.com/IPL.html + * This library 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; either + * version 2.1 of the License, or (at your option) any later version. + * You may obtain a copy of the Licence at + * http://www.gnu.org/licences/lgpl.html + * + * As a special exception this file can also be included in modules + * with other source code as long as that source code has been + * released under an Open Source Initiative certificed licence. + * More information about OSI certification can be found at: + * http://www.opensource.org + * + * This module 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 Licence for more details. + * + * This module was created by members of the firebird development + * team. All individual contributions remain the Copyright (C) of + * those individuals and all rights are reserved. Contributors to + * this file are either listed below or can be obtained from a CVS + * history command. * - * Software distributed under the License is distributed on an - * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express - * or implied. See the License for the specific language governing - * rights and limitations under the License. - * - * The Original Code was created by Inprise Corporation - * and its predecessors. Portions created by Inprise Corporation are - * Copyright (C) Inprise Corporation. - * - * Created by: Nickolay Samofatov + * Created by: Nickolay Samofatov * - * STL allocator is based on one by Mike Nordell and John Bellardo + * STL allocator is based on one by Mike Nordell and John Bellardo + * + * Contributor(s): + * + * + * $Id: alloc.h,v 1.25 2003-09-08 20:23:32 skidder Exp $ * - * All Rights Reserved. - * Contributor(s): ______________________________________. */ #ifndef ALLOC_H diff --git a/src/common/classes/class_perf.cpp b/src/common/classes/class_perf.cpp index fe02635164..591e754dc7 100644 --- a/src/common/classes/class_perf.cpp +++ b/src/common/classes/class_perf.cpp @@ -3,24 +3,37 @@ * MODULE: class_perf.cpp * DESCRIPTION: Class library performance measurements * - * The contents of this file are subject to the Interbase Public - * License Version 1.0 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy - * of the License at http://www.Inprise.com/IPL.html + * This library 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; either + * version 2.1 of the License, or (at your option) any later version. + * You may obtain a copy of the Licence at + * http://www.gnu.org/licences/lgpl.html + * + * As a special exception this file can also be included in modules + * with other source code as long as that source code has been + * released under an Open Source Initiative certificed licence. + * More information about OSI certification can be found at: + * http://www.opensource.org + * + * This module 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 Licence for more details. + * + * This module was created by members of the firebird development + * team. All individual contributions remain the Copyright (C) of + * those individuals and all rights are reserved. Contributors to + * this file are either listed below or can be obtained from a CVS + * history command. * - * Software distributed under the License is distributed on an - * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express - * or implied. See the License for the specific language governing - * rights and limitations under the License. + * Created by: Nickolay Samofatov * - * The Original Code was created by Inprise Corporation - * and its predecessors. Portions created by Inprise Corporation are - * Copyright (C) Inprise Corporation. + * Contributor(s): + * * - * Created by: Nickolay Samofatov + * $Id: class_perf.cpp,v 1.8 2003-09-08 20:23:32 skidder Exp $ * - * All Rights Reserved. - * Contributor(s): ______________________________________. */ #include "tree.h" diff --git a/src/common/classes/class_test.cpp b/src/common/classes/class_test.cpp index 2661d6070c..ae54ce7779 100644 --- a/src/common/classes/class_test.cpp +++ b/src/common/classes/class_test.cpp @@ -3,24 +3,37 @@ * MODULE: class_test.cpp * DESCRIPTION: Class library integrity tests * - * The contents of this file are subject to the Interbase Public - * License Version 1.0 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy - * of the License at http://www.Inprise.com/IPL.html + * This library 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; either + * version 2.1 of the License, or (at your option) any later version. + * You may obtain a copy of the Licence at + * http://www.gnu.org/licences/lgpl.html + * + * As a special exception this file can also be included in modules + * with other source code as long as that source code has been + * released under an Open Source Initiative certificed licence. + * More information about OSI certification can be found at: + * http://www.opensource.org + * + * This module 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 Licence for more details. + * + * This module was created by members of the firebird development + * team. All individual contributions remain the Copyright (C) of + * those individuals and all rights are reserved. Contributors to + * this file are either listed below or can be obtained from a CVS + * history command. * - * Software distributed under the License is distributed on an - * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express - * or implied. See the License for the specific language governing - * rights and limitations under the License. + * Created by: Nickolay Samofatov * - * The Original Code was created by Inprise Corporation - * and its predecessors. Portions created by Inprise Corporation are - * Copyright (C) Inprise Corporation. + * Contributor(s): + * * - * Created by: Nickolay Samofatov + * $Id: class_test.cpp,v 1.10 2003-09-08 20:23:32 skidder Exp $ * - * All Rights Reserved. - * Contributor(s): ______________________________________. */ #include "../../include/firebird.h" diff --git a/src/common/classes/locks.h b/src/common/classes/locks.h index d5cd449cf3..cc5da60337 100644 --- a/src/common/classes/locks.h +++ b/src/common/classes/locks.h @@ -3,24 +3,37 @@ * MODULE: locks.h * DESCRIPTION: Single-state locks * - * The contents of this file are subject to the Interbase Public - * License Version 1.0 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy - * of the License at http://www.Inprise.com/IPL.html + * This library 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; either + * version 2.1 of the License, or (at your option) any later version. + * You may obtain a copy of the Licence at + * http://www.gnu.org/licences/lgpl.html + * + * As a special exception this file can also be included in modules + * with other source code as long as that source code has been + * released under an Open Source Initiative certificed licence. + * More information about OSI certification can be found at: + * http://www.opensource.org + * + * This module 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 Licence for more details. + * + * This module was created by members of the firebird development + * team. All individual contributions remain the Copyright (C) of + * those individuals and all rights are reserved. Contributors to + * this file are either listed below or can be obtained from a CVS + * history command. * - * Software distributed under the License is distributed on an - * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express - * or implied. See the License for the specific language governing - * rights and limitations under the License. + * Created by: Nickolay Samofatov * - * The Original Code was created by Inprise Corporation - * and its predecessors. Portions created by Inprise Corporation are - * Copyright (C) Inprise Corporation. + * Contributor(s): + * * - * Created by: Nickolay Samofatov + * $Id: locks.h,v 1.8 2003-09-08 20:23:32 skidder Exp $ * - * All Rights Reserved. - * Contributor(s): ______________________________________. */ #ifndef LOCKS_H diff --git a/src/common/classes/rwlock.h b/src/common/classes/rwlock.h index 466638ddc1..ec23c360e5 100644 --- a/src/common/classes/rwlock.h +++ b/src/common/classes/rwlock.h @@ -3,24 +3,37 @@ * MODULE: rwlock.h * DESCRIPTION: Read/write multi-state locks * - * The contents of this file are subject to the Interbase Public - * License Version 1.0 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy - * of the License at http://www.Inprise.com/IPL.html + * This library 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; either + * version 2.1 of the License, or (at your option) any later version. + * You may obtain a copy of the Licence at + * http://www.gnu.org/licences/lgpl.html + * + * As a special exception this file can also be included in modules + * with other source code as long as that source code has been + * released under an Open Source Initiative certificed licence. + * More information about OSI certification can be found at: + * http://www.opensource.org + * + * This module 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 Licence for more details. + * + * This module was created by members of the firebird development + * team. All individual contributions remain the Copyright (C) of + * those individuals and all rights are reserved. Contributors to + * this file are either listed below or can be obtained from a CVS + * history command. * - * Software distributed under the License is distributed on an - * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express - * or implied. See the License for the specific language governing - * rights and limitations under the License. + * Created by: Nickolay Samofatov * - * The Original Code was created by Inprise Corporation - * and its predecessors. Portions created by Inprise Corporation are - * Copyright (C) Inprise Corporation. + * Contributor(s): + * * - * Created by: Nickolay Samofatov + * $Id: rwlock.h,v 1.5 2003-09-08 20:23:32 skidder Exp $ * - * All Rights Reserved. - * Contributor(s): ______________________________________. */ #ifndef RWLOCK_H diff --git a/src/common/classes/tree.h b/src/common/classes/tree.h index b4975a8746..72001d0bb1 100644 --- a/src/common/classes/tree.h +++ b/src/common/classes/tree.h @@ -3,24 +3,37 @@ * MODULE: tree.h * DESCRIPTION: Generic In-memory B+ Tree * - * The contents of this file are subject to the Interbase Public - * License Version 1.0 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy - * of the License at http://www.Inprise.com/IPL.html + * This library 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; either + * version 2.1 of the License, or (at your option) any later version. + * You may obtain a copy of the Licence at + * http://www.gnu.org/licences/lgpl.html + * + * As a special exception this file can also be included in modules + * with other source code as long as that source code has been + * released under an Open Source Initiative certificed licence. + * More information about OSI certification can be found at: + * http://www.opensource.org + * + * This module 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 Licence for more details. + * + * This module was created by members of the firebird development + * team. All individual contributions remain the Copyright (C) of + * those individuals and all rights are reserved. Contributors to + * this file are either listed below or can be obtained from a CVS + * history command. * - * Software distributed under the License is distributed on an - * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express - * or implied. See the License for the specific language governing - * rights and limitations under the License. + * Created by: Nickolay Samofatov * - * The Original Code was created by Inprise Corporation - * and its predecessors. Portions created by Inprise Corporation are - * Copyright (C) Inprise Corporation. + * Contributor(s): + * * - * Created by: Nickolay Samofatov + * $Id: tree.h,v 1.19 2003-09-08 20:23:32 skidder Exp $ * - * All Rights Reserved. - * Contributor(s): ______________________________________. */ #ifndef TREE_H diff --git a/src/common/classes/vector.h b/src/common/classes/vector.h index 31de9fd1cb..f3d72b17a0 100644 --- a/src/common/classes/vector.h +++ b/src/common/classes/vector.h @@ -3,24 +3,37 @@ * MODULE: vector.h * DESCRIPTION: static array of simple elements * - * The contents of this file are subject to the Interbase Public - * License Version 1.0 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy - * of the License at http://www.Inprise.com/IPL.html + * This library 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; either + * version 2.1 of the License, or (at your option) any later version. + * You may obtain a copy of the Licence at + * http://www.gnu.org/licences/lgpl.html + * + * As a special exception this file can also be included in modules + * with other source code as long as that source code has been + * released under an Open Source Initiative certificed licence. + * More information about OSI certification can be found at: + * http://www.opensource.org + * + * This module 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 Licence for more details. + * + * This module was created by members of the firebird development + * team. All individual contributions remain the Copyright (C) of + * those individuals and all rights are reserved. Contributors to + * this file are either listed below or can be obtained from a CVS + * history command. * - * Software distributed under the License is distributed on an - * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express - * or implied. See the License for the specific language governing - * rights and limitations under the License. + * Created by: Nickolay Samofatov * - * The Original Code was created by Inprise Corporation - * and its predecessors. Portions created by Inprise Corporation are - * Copyright (C) Inprise Corporation. + * Contributor(s): + * * - * Created by: Nickolay Samofatov + * $Id: vector.h,v 1.3 2003-09-08 20:23:32 skidder Exp $ * - * All Rights Reserved. - * Contributor(s): ______________________________________. */ #ifndef VECTOR_H diff --git a/src/dsql/parse.y b/src/dsql/parse.y index 0dad82adf7..fe5a6c7f5e 100644 --- a/src/dsql/parse.y +++ b/src/dsql/parse.y @@ -102,7 +102,7 @@ /* Can't include ../jrd/err_proto.h here because it pulls jrd.h. */ #if !defined(JRD_ERR_PROTO_H) -extern "C" TEXT *DLL_EXPORT ERR_string(const TEXT*, int); +TEXT *DLL_EXPORT ERR_string(const TEXT*, int); #endif ASSERT_FILENAME diff --git a/src/include/firebird.h b/src/include/firebird.h index eb9b892830..81da58c71c 100644 --- a/src/include/firebird.h +++ b/src/include/firebird.h @@ -30,7 +30,7 @@ * John Bellardo * * - * $Id: firebird.h,v 1.13 2003-08-10 15:43:21 skidder Exp $ + * $Id: firebird.h,v 1.14 2003-09-08 20:23:34 skidder Exp $ * */ @@ -71,4 +71,8 @@ #define MULTI_THREAD 1 #endif +#ifndef NULL +#define NULL 0L +#endif + #endif /* INCLUDE_Firebird */ diff --git a/src/jrd/cch.cpp b/src/jrd/cch.cpp index c54418a1e6..d6136e1a46 100644 --- a/src/jrd/cch.cpp +++ b/src/jrd/cch.cpp @@ -187,6 +187,143 @@ static void unmark(TDBB, WIN *); #define DUMMY_CHECKSUM 12345 +bool set_write_direction(DBB dbb, BDB bdb, SSHORT direction) { +#ifdef SUPERSERVER + if (bdb->bdb_write_direction == BDB_write_normal || + bdb->bdb_write_direction == BDB_write_both) + { + if (direction != BDB_write_normal && direction != BDB_write_both) + dbb->backup_manager->release_sw_database_lock(); + } else { + if (direction == BDB_write_normal || direction == BDB_write_both) + dbb->backup_manager->get_sw_database_lock(true, true); + } + bdb->bdb_write_direction = direction; +#else + LCK_ast_inhibit(); + switch(bdb->bdb_write_direction) { + case BDB_write_normal: + case BDB_write_both: + switch (direction) { + case BDB_write_diff: + dbb->backup_manager->increment_diff_use_count(); + dbb->backup_manager->release_sw_database_lock(); + break; + case BDB_write_undefined: + dbb->backup_manager->release_sw_database_lock(); + break; + } + break; + case BDB_write_diff: + switch(direction) { + case BDB_write_normal: + case BDB_write_both: + dbb->backup_manager->decrement_diff_use_count(); + bdb->bdb_write_direction = direction; + // We ask this function to enable signals + if (!dbb->backup_manager->get_sw_database_lock(true, true)) { + bdb->bdb_write_direction = BDB_write_undefined; + return false; + } + return true; + case BDB_write_undefined: + dbb->backup_manager->decrement_diff_use_count(); + } + break; + case BDB_write_undefined: + switch (direction) { + case BDB_write_diff: + dbb->backup_manager->increment_diff_use_count(); + break; + case BDB_write_normal: + case BDB_write_both: + bdb->bdb_write_direction = direction; + // We ask this function to enable signals + if (!dbb->backup_manager->get_sw_database_lock(true, true)) { + bdb->bdb_write_direction = BDB_write_undefined; + return false; + } + return true; + } + break; + } + bdb->bdb_write_direction = direction; + LCK_ast_enable(); +#endif + return true; +} + +void CCH_flush_database(TDBB tdbb) { +/************************************** + * + * C C H _ f l u s h _ d a t a b a s e + * + ************************************** + * + * Functional description + * Flush all buffers coming from database file. + * May be called from AST + * + **************************************/ + SET_TDBB(tdbb); + DBB dbb = tdbb->tdbb_database; + + BCB bcb = dbb->dbb_bcb; + +#ifdef SUPERSERVER + // This is called on architectures with shared buffer cache (like SuperServer) + + // Determine a list of pages to flush + Firebird::BePlusTree pages(tdbb->tdbb_default); + BCB_MUTEX_ACQUIRE; + try { + for (ULONG i = 0; (bcb = dbb->dbb_bcb) && i < bcb->bcb_count; i++) { + BDB bdb = bcb->bcb_rpt[i].bcb_bdb; + if (bdb->bdb_length) + continue; + if (!(bdb->bdb_flags & (BDB_dirty | BDB_db_dirty)) || + bdb->bdb_write_direction == BDB_write_diff) continue; + // As the result we can add a number of redundant pages to flush + // that were marked as dirty, but not released yet. This should + // not create problems except that we'll loose some time later + // during the flush itself + pages.add(bdb->bdb_page); + } + } catch(std::exception&) { + // Handle out-of-memory error + BCB_MUTEX_RELEASE; + throw; + } + BCB_MUTEX_RELEASE; + + // Now, apply force to flush this pages. Flush pages in order. + if (pages.getFirst()) do { + WIN window; + window.win_page = pages.current(); + window.win_flags = 0; + PAG page = CCH_FETCH(tdbb, &window, LCK_write, pag_undefined); + CCH_MARK_MUST_WRITE(tdbb, &window); + CCH_RELEASE(tdbb, &window); + } while(pages.getNext()); +#else + /* Do some fancy footwork to make sure that pages are + not removed from the btc tree at AST level. Then + restore the flag to whatever it was before. */ + + bool keep_pages = bcb->bcb_flags & BCB_keep_pages; + dbb->dbb_bcb->bcb_flags |= BCB_keep_pages; + for (ULONG i = 0; (bcb = dbb->dbb_bcb) && i < bcb->bcb_count; i++) { + BDB bdb = bcb->bcb_rpt[i].bcb_bdb; + if (bdb->bdb_length) + continue; + if (!(bdb->bdb_flags & (BDB_dirty | BDB_db_dirty)) || + bdb->bdb_write_direction == BDB_write_diff) continue; + down_grade(tdbb, bdb); + } + if (!keep_pages) + bcb->bcb_flags &= ~BCB_keep_pages; +#endif +} USHORT CCH_checksum(BDB bdb) { @@ -920,27 +1057,23 @@ void CCH_fetch_page( persistant (more than 3 times) error out of the routine by calling CCH_unwind, and eventually punting out. */ - if (!dbb->backup_manager->lock_state(false) || - !dbb->backup_manager->actualize_state(status)) + if (!dbb->backup_manager->lock_state(false)) { PAGE_LOCK_RELEASE(bdb->bdb_lock); - dbb->backup_manager->unlock_state(); CCH_unwind(tdbb, TRUE); } int bak_state = dbb->backup_manager->get_state(); ULONG diff_page = 0; if (bak_state == nbak_state_stalled || bak_state == nbak_state_merge) { - if (!dbb->backup_manager->lock_alloc(false) || - !dbb->backup_manager->actualize_alloc(status)) + if (!dbb->backup_manager->lock_alloc(false)) { PAGE_LOCK_RELEASE(bdb->bdb_lock); - dbb->backup_manager->unlock_alloc(); dbb->backup_manager->unlock_state(); CCH_unwind(tdbb, TRUE); } diff_page = dbb->backup_manager->get_page_index(bdb->bdb_page); dbb->backup_manager->unlock_alloc(); - TRACE3("Reading page %d, state=%d, diff page=%d", bdb->bdb_page, bak_state, diff_page); + NBAK_TRACE(("Reading page %d, state=%d, diff page=%d", bdb->bdb_page, bak_state, diff_page)); } if (bak_state == nbak_state_normal || @@ -951,7 +1084,8 @@ void CCH_fetch_page( (!diff_page || (bdb->bdb_page < dbb->backup_manager->get_backup_pages()))) ) { - TRACE3("Reading page %d, state=%d, diff page=%d from DISK", bdb->bdb_page, bak_state, diff_page); + NBAK_TRACE(("Reading page %d, state=%d, diff page=%d from DISK", + bdb->bdb_page, bak_state, diff_page)); // Read page from disk as normal while (!PIO_read(file, bdb, page, status)) { if (!read_shadow) { @@ -989,12 +1123,47 @@ void CCH_fetch_page( page->pag_scn() < dbb->backup_manager->get_current_scn()) ))) { - TRACE3("Reading page %d, state=%d, diff page=%d from DIFFERENCE", bdb->bdb_page, bak_state, diff_page); - if (!dbb->backup_manager->read_difference(status, diff_page, page)) { + NBAK_TRACE(("Reading page %d, state=%d, diff page=%d from DIFFERENCE", + bdb->bdb_page, bak_state, diff_page)); + if (!dbb->backup_manager->read_difference(diff_page, page)) { PAGE_LOCK_RELEASE(bdb->bdb_lock); dbb->backup_manager->unlock_state(); CCH_unwind(tdbb, TRUE); } + if (page->pag_type == pag_undefined) { + // Page was marked as allocated inside the difference file, but not really used + // this is very rare, but possible case (after certain errors). + // Read (or re-read) page from database + NBAK_TRACE(("Re-reading page %d, state=%d, diff page=%d from DISK", + bdb->bdb_page, bak_state, diff_page)); + while (!PIO_read(file, bdb, page, status)) { + if (!read_shadow) { + break; + } +#ifdef SUPERSERVER + THREAD_ENTER; +#endif + if (!CCH_rollover_to_shadow(dbb, file, FALSE)) { + PAGE_LOCK_RELEASE(bdb->bdb_lock); + dbb->backup_manager->unlock_state(); + CCH_unwind(tdbb, TRUE); + } + if (file != dbb->dbb_file) + file = dbb->dbb_file; + else { + if (retryCount++ == 3) { + ib_fprintf(ib_stderr, + "IO error loop Unwind to avoid a hang\n"); + PAGE_LOCK_RELEASE(bdb->bdb_lock); + dbb->backup_manager->unlock_state(); + CCH_unwind(tdbb, TRUE); + } + } +#ifdef SUPERSERVER + THREAD_EXIT; +#endif + } + } } dbb->backup_manager->unlock_state(); @@ -2128,6 +2297,18 @@ void CCH_recover_shadow(TDBB tdbb, SBM sbm_rec) } +void invalidate_and_release_buffer(TDBB tdbb, BDB bdb) { + // This function should be called before difference processing is done. + // So there should be no need to no need to release difference locks though + DBB dbb = tdbb->tdbb_database; + bdb->bdb_flags |= BDB_not_valid; + bdb->bdb_flags &= ~BDB_dirty; + set_write_direction(dbb, bdb, BDB_write_undefined); + TRA_invalidate(dbb, bdb->bdb_transactions); + bdb->bdb_transactions = 0; + release_bdb(tdbb, bdb, FALSE, FALSE, FALSE); +} + void CCH_release(TDBB tdbb, WIN * window, BOOLEAN release_tail) { /************************************** @@ -2145,7 +2326,6 @@ void CCH_release(TDBB tdbb, WIN * window, BOOLEAN release_tail) DBB dbb; BDB bdb; SSHORT use_count; - BOOLEAN marked; SET_TDBB(tdbb); dbb = tdbb->tdbb_database; @@ -2173,8 +2353,92 @@ void CCH_release(TDBB tdbb, WIN * window, BOOLEAN release_tail) if (bdb->bdb_use_count == 1) { - marked = (bdb->bdb_flags & BDB_marked) ? TRUE : FALSE; - bdb->bdb_flags &= ~(BDB_writer | BDB_marked | BDB_faked); + if (bdb->bdb_flags & BDB_dirty) { + SSHORT write_direction; + // We are going to release a page that is dirty. Lets determine + // location of the page in difference file and write destination + // so BDB AST handlers can safely use this information + if (!dbb->backup_manager->lock_state(true)) { + invalidate_and_release_buffer(tdbb, bdb); + CCH_unwind(tdbb, TRUE); + } +#ifndef SUPERSERVER + bdb->bdb_diff_generation = dbb->backup_manager->get_current_generation(); +#endif + if (bdb->bdb_page != HEADER_PAGE) // SCN of header page is adjusted in nbak.cpp + bdb->bdb_buffer->pag_scn() = dbb->backup_manager->get_current_scn(); // Set SCN for the page + int backup_state = dbb->backup_manager->get_state(); + switch (backup_state) { + case nbak_state_normal: + write_direction = BDB_write_normal; + break; + case nbak_state_stalled: + write_direction = BDB_write_diff; + break; + case nbak_state_merge: + if (tdbb->tdbb_flags & TDBB_backup_merge || + bdb->bdb_page < dbb->backup_manager->get_backup_pages()) + { + write_direction = BDB_write_normal; + } else + write_direction = BDB_write_both; + break; + } + switch (write_direction) { + case BDB_write_diff: + if (!dbb->backup_manager->lock_alloc(true)) { + dbb->backup_manager->unlock_state(); + invalidate_and_release_buffer(tdbb, bdb); + CCH_unwind(tdbb, TRUE); + } + bdb->bdb_difference_page = dbb->backup_manager->get_page_index(bdb->bdb_page); + dbb->backup_manager->unlock_alloc(); + if (!bdb->bdb_difference_page) { + if (!dbb->backup_manager->lock_alloc_write(true)) { + dbb->backup_manager->unlock_state(); + invalidate_and_release_buffer(tdbb, bdb); + CCH_unwind(tdbb, TRUE); + } + bdb->bdb_difference_page = dbb->backup_manager->allocate_difference_page(bdb->bdb_page); + dbb->backup_manager->unlock_alloc_write(); + if (!bdb->bdb_difference_page) { + dbb->backup_manager->unlock_state(); + invalidate_and_release_buffer(tdbb, bdb); + CCH_unwind(tdbb, TRUE); + } + NBAK_TRACE(("Allocate difference page %d for database page %d", + bdb->bdb_difference_page, bdb->bdb_page)); + } else { + NBAK_TRACE(("Map existing difference page %d to database page %d", + bdb->bdb_difference_page, bdb->bdb_page)); + } + break; + case BDB_write_both: + if (!dbb->backup_manager->lock_alloc(true)) { + dbb->backup_manager->unlock_state(); + invalidate_and_release_buffer(tdbb, bdb); + CCH_unwind(tdbb, TRUE); + } + bdb->bdb_difference_page = dbb->backup_manager->get_page_index(bdb->bdb_page); + dbb->backup_manager->unlock_alloc(); + if (bdb->bdb_difference_page) { + NBAK_TRACE(("Map existing difference page %d to database page %d (write_both)", + bdb->bdb_difference_page, bdb->bdb_page)); + } else { + // This may really happen. Database file can grow while in merge mode + write_direction = BDB_write_normal; + } + break; + } + if (!set_write_direction(dbb, bdb, write_direction)) { + dbb->backup_manager->unlock_state(); + invalidate_and_release_buffer(tdbb, bdb); + CCH_unwind(tdbb, TRUE); + } + dbb->backup_manager->unlock_state(); + } + bool marked = bdb->bdb_flags & BDB_marked; + bdb->bdb_flags &= ~(BDB_writer | BDB_marked | BDB_faked); if (marked) { release_bdb(tdbb, bdb, FALSE, FALSE, TRUE); @@ -2411,7 +2675,6 @@ void DLL_EXPORT CCH_shutdown_database(DBB dbb) #endif } - void CCH_unwind(TDBB tdbb, BOOLEAN punt) { /************************************** @@ -2469,9 +2732,11 @@ void CCH_unwind(TDBB tdbb, BOOLEAN punt) (page->pag_type == pag_transactions)) { ++bdb->bdb_use_count; - bdb->bdb_flags &= - ~(BDB_dirty | BDB_writer | BDB_marked | BDB_faked | - BDB_db_dirty); + // Adjust backup page locks + if (bdb->bdb_flags & BDB_dirty) + set_write_direction(dbb, bdb, BDB_write_undefined); + bdb->bdb_flags &= ~(BDB_dirty | + BDB_writer | BDB_marked | BDB_faked | BDB_db_dirty); PAGE_LOCK_RELEASE(bdb->bdb_lock); --bdb->bdb_use_count; } @@ -3690,7 +3955,11 @@ static BOOLEAN down_grade(TDBB tdbb, BDB bdb) if (dbb->dbb_flags & DBB_bugcheck) { PAGE_LOCK_RELEASE(bdb->bdb_lock); bdb->bdb_ast_flags &= ~BDB_blocking; - bdb->bdb_flags &= ~BDB_dirty; + // Release backup pages lock as buffer is no longer dirty + if (bdb->bdb_flags & BDB_dirty) { + bdb->bdb_flags &= ~BDB_dirty; + set_write_direction(dbb, bdb, BDB_write_undefined); + } return TRUE; } @@ -3760,7 +4029,9 @@ static BOOLEAN down_grade(TDBB tdbb, BDB bdb) if (invalid || !write_page(tdbb, bdb, FALSE, tdbb->tdbb_status_vector, TRUE)) { bdb->bdb_flags |= BDB_not_valid; + // Release backup pages lock bdb->bdb_flags &= ~BDB_dirty; + set_write_direction(dbb, bdb, BDB_write_undefined); bdb->bdb_ast_flags &= ~BDB_blocking; TRA_invalidate(dbb, bdb->bdb_transactions); bdb->bdb_transactions = 0; @@ -5614,92 +5885,48 @@ static BOOLEAN write_page( /* write out page to main database file, and to any shadows, making a special case of the header page */ - if (bdb->bdb_page >= 0) { - if (!dbb->backup_manager->lock_state(true) || - !dbb->backup_manager->actualize_state(status)) - { - bdb->bdb_flags |= BDB_io_error; - dbb->dbb_flags |= DBB_suspend_bgio; - dbb->backup_manager->unlock_state(); + if (bdb->bdb_page >= 0) { + if (bdb->bdb_write_direction == BDB_write_undefined) { + dbb->dbb_flags |= DBB_bugcheck; + status[0] = isc_arg_gds; + status[1] = gds_bug_check; + status[2] = gds_arg_string; + status[3] = (ISC_STATUS)ERR_cstring("Undefined page write direction"); + status[4] = gds_arg_end; return FALSE; } - if (bdb->bdb_page != HEADER_PAGE) // SCN of header page is adjusted in nbak.cpp - page->pag_scn() = dbb->backup_manager->get_current_scn(); // Set SCN for the page page->pag_checksum = CCH_checksum(bdb); - int backup_state = dbb->backup_manager->get_state(); - if (backup_state == nbak_state_stalled || - // We write pages that were beyond the end of file to difference file too - // This is because we cannot read page from main database file to choose - (backup_state == nbak_state_merge && !(tdbb->tdbb_flags & TDBB_backup_merge) && - bdb->bdb_page >= dbb->backup_manager->get_backup_pages())) + if (bdb->bdb_write_direction == BDB_write_diff || + (bdb->bdb_write_direction == BDB_write_both +#ifndef SUPERSERVER + && bdb->bdb_diff_generation == dbb->backup_manager->get_current_generation() +#endif + )) { - // Write to difference file - if (!dbb->backup_manager->lock_alloc(true) || - !dbb->backup_manager->actualize_alloc(status)) +#ifdef NBAK_DEBUG + // We cannot call normal trace functions here as they are signal-unsafe + char buffer[1000], *ptr = buffer; + strcpy(ptr, "NBAK,Write page "); ptr += strlen(ptr); + gds__ulstr(ptr, bdb->bdb_page, 0, 0); ptr += strlen(ptr); + strcpy(ptr, " at offset "); ptr += strlen(ptr); + gds__ulstr(ptr, bdb->bdb_difference_page, 0, 0); ptr += strlen(ptr); + strcpy(ptr, " in difference file"); + gds__trace(buffer); +#endif + if (!dbb->backup_manager->write_difference( + status, bdb->bdb_difference_page, bdb->bdb_buffer)) { - dbb->backup_manager->unlock_alloc(); bdb->bdb_flags |= BDB_io_error; dbb->dbb_flags |= DBB_suspend_bgio; - dbb->backup_manager->unlock_state(); return FALSE; } - diff_page = dbb->backup_manager->get_page_index(bdb->bdb_page); - dbb->backup_manager->unlock_alloc(); - if (diff_page) { - // Simple case. Write to difference file directly - if (!dbb->backup_manager->write_difference(status, diff_page, bdb->bdb_buffer)) { - bdb->bdb_flags |= BDB_io_error; - dbb->dbb_flags |= DBB_suspend_bgio; - dbb->backup_manager->unlock_state(); - return FALSE; - } - TRACE2("Write page %u at offset %u (existing) in difference file", bdb->bdb_page, diff_page); - } else if (backup_state == nbak_state_stalled) { - if (!dbb->backup_manager->lock_alloc_write(true) || - !dbb->backup_manager->actualize_alloc(status)) - { - dbb->backup_manager->unlock_alloc_write(); - bdb->bdb_flags |= BDB_io_error; - dbb->dbb_flags |= DBB_suspend_bgio; - dbb->backup_manager->unlock_state(); - return FALSE; - } - diff_page = dbb->backup_manager->get_page_index(bdb->bdb_page); - if (diff_page) { - if (!dbb->backup_manager->write_difference(status, diff_page, bdb->bdb_buffer)) { - dbb->backup_manager->unlock_alloc_write(); - bdb->bdb_flags |= BDB_io_error; - dbb->dbb_flags |= DBB_suspend_bgio; - dbb->backup_manager->unlock_state(); - return FALSE; - } - TRACE2("Write page %u at offset %u (appeared) in difference file", bdb->bdb_page, diff_page); - } else { - diff_page = dbb->backup_manager->get_next_page(); - if (!dbb->backup_manager->write_difference(status, diff_page, bdb->bdb_buffer)) { - dbb->backup_manager->unlock_alloc_write(); - bdb->bdb_flags |= BDB_io_error; - dbb->dbb_flags |= DBB_suspend_bgio; - dbb->backup_manager->unlock_state(); - return FALSE; - } - TRACE2("Write page %u at offset %u (created) in difference file", bdb->bdb_page, diff_page); - if (!dbb->backup_manager->mark_alloc(status, bdb->bdb_page)) { - dbb->backup_manager->unlock_alloc_write(); - bdb->bdb_flags |= BDB_io_error; - dbb->dbb_flags |= DBB_suspend_bgio; - dbb->backup_manager->unlock_state(); - return FALSE; - } - } - dbb->backup_manager->unlock_alloc_write(); - } } - if (backup_state == nbak_state_stalled) { + if (bdb->bdb_write_direction == BDB_write_diff) { // We finished. Adjust transaction accounting and get ready for exit if (bdb->bdb_page == HEADER_PAGE) dbb->dbb_last_header_write = ((HDR) page)->hdr_next_transaction; + set_write_direction(dbb, bdb, BDB_write_undefined); } else { // We need to write our pages to main database files #ifdef SUPERSERVER @@ -5713,7 +5940,6 @@ static BOOLEAN write_page( if (!CCH_rollover_to_shadow(dbb, file, inAst)) { bdb->bdb_flags |= BDB_io_error; dbb->dbb_flags |= DBB_suspend_bgio; - dbb->backup_manager->unlock_state(); return FALSE; } #ifdef SUPERSERVER @@ -5731,9 +5957,8 @@ static BOOLEAN write_page( if (dbb->dbb_shadow) result = CCH_write_all_shadows(tdbb, 0, bdb, status, 0, inAst); + set_write_direction(dbb, bdb, BDB_write_undefined); } - dbb->backup_manager->unlock_state(); - } #ifdef SUPERSERVER diff --git a/src/jrd/cch.h b/src/jrd/cch.h index 337d39df53..a4d53cdbb5 100644 --- a/src/jrd/cch.h +++ b/src/jrd/cch.h @@ -95,9 +95,14 @@ class bdb : public pool_alloc struct tdbb*bdb_io; /* thread holding io latch */ UATOM bdb_ast_flags; /* flags manipulated at AST level */ USHORT bdb_flags; - USHORT bdb_length; /* Length of journal records */ - SSHORT bdb_use_count; /* Number of active users */ - SSHORT bdb_scan_count; /* concurrent sequential scans */ + USHORT bdb_length; /* Length of journal records */ + SSHORT bdb_use_count; /* Number of active users */ + SSHORT bdb_scan_count; /* concurrent sequential scans */ + USHORT bdb_write_direction; /* Where to write buffer */ + ULONG bdb_difference_page; /* Number of page in difference file */ + SLONG bdb_diff_generation; /* Number of backup/restore cycle for + this database in current process. + Used in CS only. */ struct tdbb*bdb_shared[BDB_max_shared]; /* threads holding shared latches */ }; typedef bdb *BDB; @@ -125,6 +130,13 @@ typedef bdb *BDB; #define BDB_blocking 1 /* a blocking ast was sent while page locked */ +/* bdb_write_direction values */ + +#define BDB_write_undefined 0 +#define BDB_write_normal 1 +#define BDB_write_diff 2 +#define BDB_write_both 3 + /* PRE -- Precedence block */ diff --git a/src/jrd/cch_proto.h b/src/jrd/cch_proto.h index 6f818c5fe4..3531b86a86 100644 --- a/src/jrd/cch_proto.h +++ b/src/jrd/cch_proto.h @@ -67,6 +67,7 @@ void CCH_release_journal(TDBB, SLONG); BOOLEAN CCH_rollover_to_shadow(struct dbb *, struct fil *, BOOLEAN); void CCH_unwind(TDBB, BOOLEAN); BOOLEAN CCH_validate(struct win *); +void CCH_flush_database(TDBB tdbb); BOOLEAN CCH_write_all_shadows(TDBB, struct sdw *, struct bdb *, ISC_STATUS *, USHORT, BOOLEAN); diff --git a/src/jrd/common.h b/src/jrd/common.h index 948b1f82c4..118a0f159b 100644 --- a/src/jrd/common.h +++ b/src/jrd/common.h @@ -49,7 +49,7 @@ * */ /* -$Id: common.h,v 1.80 2003-09-01 07:58:04 brodsom Exp $ +$Id: common.h,v 1.81 2003-09-08 20:23:35 skidder Exp $ */ #ifndef JRD_COMMON_H @@ -73,8 +73,6 @@ $Id: common.h,v 1.80 2003-09-01 07:58:04 brodsom Exp $ #include "../include/fb_types.h" #endif - - /* do not use links in source code to maintain platform neutraility */ @@ -129,7 +127,6 @@ $Id: common.h,v 1.80 2003-09-01 07:58:04 brodsom Exp $ #define MOVE_FASTER(from,to,length) memcpy (to, from, (int) (length)) #define MOVE_CLEAR(to,length) memset (to, 0, (int) (length)) -typedef RETSIGTYPE (*SIG_FPTR) (int); #endif /* LINUX */ /***************************************************** @@ -205,8 +202,6 @@ int syslog(int pri, char *fmt, ...); #define MOVE_FASTER(from,to,length) memcpy (to, from, (int) (length)) #define MOVE_CLEAR(to,length) memset (to, 0, (int) (length)) -typedef RETSIGTYPE (*SIG_FPTR) (); - //format for __LINE__ #define LINEFORMAT "d" @@ -250,7 +245,6 @@ typedef RETSIGTYPE (*SIG_FPTR) (); #define MOVE_FASTER(from,to,length) memcpy (to, from, (int) (length)) #define MOVE_CLEAR(to,length) memset (to, 0, (int) (length)) -typedef RETSIGTYPE (*SIG_FPTR) (int); #endif /* Darwin Platforms */ @@ -283,7 +277,6 @@ typedef RETSIGTYPE (*SIG_FPTR) (int); #define MOVE_FASTER(from,to,length) memcpy (to, from, (int) (length)) #define MOVE_CLEAR(to,length) memset (to, 0, (int) (length)) -typedef RETSIGTYPE (*SIG_FPTR) (int); #endif /* FREEBSD */ /***************************************************** @@ -315,7 +308,6 @@ typedef RETSIGTYPE (*SIG_FPTR) (int); #define MOVE_FASTER(from,to,length) memcpy (to, from, (int) (length)) #define MOVE_CLEAR(to,length) memset (to, 0, (int) (length)) -typedef RETSIGTYPE (*SIG_FPTR) (); #endif /* NETBSD */ @@ -433,7 +425,6 @@ typedef RETSIGTYPE (*SIG_FPTR) (); #define MOVE_FASTER(from,to,length) memcpy (to, from, (int) (length)) #define MOVE_CLEAR(to,length) memset (to, 0, (int) (length)) -typedef RETSIGTYPE (*SIG_FPTR) (int); #endif /* sun */ @@ -474,7 +465,6 @@ typedef RETSIGTYPE (*SIG_FPTR) (int); #define MOVE_FASTER(from,to,length) memcpy (to, from, (int) (length)) #define MOVE_CLEAR(to,length) memset (to, 0, (int) (length)) -typedef RETSIGTYPE (*SIG_FPTR) (); #endif /* hpux */ @@ -503,7 +493,6 @@ typedef unsigned int64 UATOM; #define FINI_ERROR 44 #define STARTUP_ERROR 46 /* this is also used in iscguard.h, make sure these match */ -typedef RETSIGTYPE (*SIG_FPTR) (); #endif /* VMS */ @@ -546,7 +535,6 @@ typedef RETSIGTYPE (*SIG_FPTR) (); #endif /* IBM PowerPC */ -typedef RETSIGTYPE (*SIG_FPTR) (); #endif /* IBM AIX */ @@ -616,7 +604,6 @@ typedef unsigned __int64 UINT64; #endif #endif -typedef RETSIGTYPE (CLIB_ROUTINE * SIG_FPTR) (int); #endif /* WIN_NT */ // 23 Sep 2002, skidder, ALLOC_LIB_MEMORY moved here, @@ -653,7 +640,6 @@ typedef RETSIGTYPE (CLIB_ROUTINE * SIG_FPTR) (int); #define setregid(rgid,egid) setgid(egid) */ -typedef RETSIGTYPE (*SIG_FPTR) (); #endif /* SCO_EV */ /***************************************************** @@ -721,10 +707,6 @@ typedef RETSIGTYPE (*SIG_FPTR) (); #define STARTUP_ERROR 2 /* this is also used in iscguard.h, make sure these match */ #endif -#ifndef NULL -#define NULL 0L -#endif - #ifndef TRUE #define TRUE 1 #endif @@ -904,28 +886,33 @@ typedef struct #define FREE_LIB_MEMORY(block) gds__free (block) #endif +// This macros are used to workaround shortage of standard conformance +// in Microsoft compilers. They could be replaced with normal procedure +// and generic macro if MSVC would support C99-style __VA_ARGS__ +#define DEFINE_TRACE_ROUTINE(routine) void routine(const char* message, ...) +#define IMPLEMENT_TRACE_ROUTINE(routine, subsystem) \ +void routine(const char* message, ...) { \ + static const char name_facility[] = subsystem ","; \ + char buffer[1000]; \ + strcpy(buffer, name_facility); \ + char *ptr = buffer + sizeof(name_facility)-1; \ + va_list params; \ + va_start(params, message); \ + vsnprintf(ptr, sizeof(buffer)-sizeof(name_facility), message, params); \ + va_end(params); \ + gds__trace(buffer); \ +} #ifdef DEV_BUILD /* Define any debugging symbols and macros here. This ifdef will be executed during development builds. */ -//#ifdef WIN_NT -#define TRACE(msg) gds__log (msg) -#define TRACE1(msg,p1) gds__log (msg,p1) -#define TRACE2(msg,p1,p2) gds__log (msg,p1,p2) -#define TRACE3(msg,p1,p2,p3) gds__log (msg,p1,p2,p3) -#define TRACE4(msg,p1,p2,p3,p4) gds__log (msg,p1,p2,p3,p4) -#define DEV_REPORT(msg) gds__log (msg) -//#endif +#define TRACE(msg) gds__trace (msg) -#ifndef TRACE -#define TRACE(msg) ib_fprintf (ib_stderr,msg) -#define TRACE1(msg,p1) ib_fprintf (ib_stderr,msg,p1) -#define TRACE2(msg,p1,p2) ib_fprintf (ib_stderr,msg,p1,p2) -#define TRACE3(msg,p1,p2,p3) ib_fprintf (ib_stderr,msg,p1,p2,p3) -#define TRACE4(msg,p1,p2,p3,p4) ib_fprintf (ib_stderr,msg,p1,p2,p3,p4) +#ifdef WIN_NT +#define DEV_REPORT(msg) gds__log (msg) #endif #ifndef DEV_REPORT @@ -947,13 +934,15 @@ void GDS_breakpoint(int); #endif /* DEV_BUILD */ #ifndef DEV_BUILD +#ifndef DEV_REPORT #define DEV_REPORT(msg) gds__log (msg) +#endif +#ifndef BREAKPOINT #define BREAKPOINT(x) /* nothing */ +#endif +#ifndef TRACE #define TRACE(msg) /* nothing */ -#define TRACE1(msg,p1) /* nothing */ -#define TRACE2(msg,p1,p2) /* nothing */ -#define TRACE3(msg,p1,p2,p3) /* nothing */ -#define TRACE4(msg,p1,p2,p3,p4) /* nothing */ +#endif #endif diff --git a/src/jrd/dfw.epp b/src/jrd/dfw.epp index b88c08ce14..c8d704a717 100644 --- a/src/jrd/dfw.epp +++ b/src/jrd/dfw.epp @@ -1090,10 +1090,8 @@ static bool add_difference( TDBB tdbb, SSHORT phase, DFW work, JRD_TRA transacti return true; case 3: if (!dbb->backup_manager->lock_state(true)) - ERR_post(gds_lock_conflict, 0); + ERR_punt(); try { - if (!dbb->backup_manager->actualize_state(tdbb->tdbb_status_vector)) - ERR_punt(); if (dbb->backup_manager->get_state() != nbak_state_normal) { ERR_post(gds_no_meta_update, @@ -1141,17 +1139,15 @@ static bool delete_difference( TDBB tdbb, return true; case 3: if (!dbb->backup_manager->lock_state(true)) - ERR_post(gds_lock_conflict, 0); + ERR_punt(); try { - if (!dbb->backup_manager->actualize_state(tdbb->tdbb_status_vector)) - ERR_punt(); if (dbb->backup_manager->get_state() != nbak_state_normal) { ERR_post(gds_no_meta_update, gds_arg_gds, gds_wrong_backup_state, 0); } dbb->backup_manager->set_difference(NULL); - } catch(const std::exception&){ + } catch(const std::exception&) { dbb->backup_manager->unlock_state(); throw; } diff --git a/src/jrd/err.cpp b/src/jrd/err.cpp index 1862a83487..76e03cf478 100644 --- a/src/jrd/err.cpp +++ b/src/jrd/err.cpp @@ -56,9 +56,6 @@ #define JRD_FAILURE_UNKNOWN "" /* Used when buffer fails */ -extern "C" { - - static TEXT* jrd_failures = NULL; static TEXT* jrd_failures_ptr = NULL; @@ -645,5 +642,3 @@ static void internal_error(ISC_STATUS status, int number) ERR_post(status, gds_arg_string, ERR_cstring(errmsg), 0); } #endif - -} /* extern "C" */ diff --git a/src/jrd/err_proto.h b/src/jrd/err_proto.h index ed7fa22f1a..be1bfd8cc0 100644 --- a/src/jrd/err_proto.h +++ b/src/jrd/err_proto.h @@ -43,10 +43,6 @@ typedef enum idx_e { #define CORRUPT(number) ERR_corrupt (number) #define IBERROR(number) ERR_error (number) -#ifdef __cplusplus -extern "C" { -#endif - BOOLEAN DLL_EXPORT ERR_post_warning(ISC_STATUS, ...); void ERR_assert(const TEXT*, int); void DLL_EXPORT ERR_bugcheck(int); @@ -60,21 +56,9 @@ void DLL_EXPORT ERR_punt(void); void DLL_EXPORT ERR_warning(ISC_STATUS, ...); void DLL_EXPORT ERR_log(int, int, const TEXT *); -#ifdef __cplusplus -} /* extern "C" */ -#endif - #endif /* REQUESTER */ -#ifdef __cplusplus -extern "C" { -#endif - const TEXT* DLL_EXPORT ERR_cstring(const TEXT*); const TEXT* DLL_EXPORT ERR_string(const TEXT*, int); -#ifdef __cplusplus -} /* extern "C" */ -#endif - #endif /* JRD_ERR_PROTO_H */ diff --git a/src/jrd/event.cpp b/src/jrd/event.cpp index 93006f682e..c57ab330ab 100644 --- a/src/jrd/event.cpp +++ b/src/jrd/event.cpp @@ -42,7 +42,8 @@ #include "../jrd/isc_s_proto.h" #include "../jrd/sch_proto.h" #include "../jrd/thd_proto.h" -#include "../jrd/isc_i_proto.h" +#include "../jrd/err_proto.h" +#include "../jrd/os/isc_i_proto.h" #include "../common/config/config.h" #ifdef HAVE_SYS_TYPES_H @@ -53,14 +54,6 @@ #include #endif -#pragma FB_COMPILER_MESSAGE("FIXFIXFIX!!! - DANGER!") -// We currently can't include jrd/err_proto.h to get the function -// declaration. :-< -extern "C" { -extern void DLL_EXPORT ERR_bugcheck_msg(const TEXT *); -} - - #ifdef UNIX #include #define EVENT_SIGNAL SIGUSR2 @@ -100,7 +93,7 @@ static void delete_event(EVNT); static void delete_process(SLONG); static void delete_request(EVT_REQ); static void delete_session(SLONG); -static AST_TYPE deliver(void); +static AST_TYPE deliver(void* arg); static void deliver_request(EVT_REQ); static void exit_handler(void *); static EVNT find_event(USHORT, TEXT *, EVNT); @@ -984,7 +977,7 @@ static void delete_session(SLONG session_id) } -static AST_TYPE deliver(void) +static AST_TYPE deliver(void* arg) { /************************************** * @@ -1676,7 +1669,7 @@ static void THREAD_ROUTINE watcher_thread(void *dummy) value = ISC_event_clear(process->prb_event); RELEASE; - deliver(); + deliver(NULL); ACQUIRE; process = (PRB) ABS_PTR(EVENT_process_offset); RELEASE; diff --git a/src/jrd/gds.cpp b/src/jrd/gds.cpp index ec88c9e887..a95474b114 100644 --- a/src/jrd/gds.cpp +++ b/src/jrd/gds.cpp @@ -52,6 +52,7 @@ #include "../jrd/iberr.h" #include "../jrd/gds_proto.h" #include "../jrd/os/path_utils.h" +#include "../jrd/misc_proto.h" #ifdef HAVE_UNISTD_H #include @@ -165,7 +166,7 @@ static const char * FB_PID_FILE = "fb_%d"; #include "../jrd/gds_proto.h" #include "../jrd/isc_proto.h" #ifndef REQUESTER -#include "../jrd/isc_i_proto.h" +#include "../jrd/os/isc_i_proto.h" #endif #ifdef WIN_NT @@ -496,6 +497,27 @@ static const UCHAR #endif +// This function is very crude, but signal-safe. +void gds__ulstr(char* buffer, ULONG value, int maxlen, char filler) { + ULONG n = value; + int c = 0; + while (n) { + n = n/10; + c++; + } + if (maxlen > c) + c = maxlen; + char *p = buffer + c; + while (value) { + *--p = '0' + (value % 10); + value = value/10; + } + while (p != buffer) { + *--p = filler; + } + buffer[c] = 0; +} + ISC_STATUS API_ROUTINE gds__decode(ISC_STATUS code, USHORT* fac, USHORT* class_) { /************************************** @@ -1036,6 +1058,95 @@ void API_ROUTINE gds__interprete_a( } +#define SECS_PER_HOUR (60 * 60) +#define SECS_PER_DAY (SECS_PER_HOUR * 24) + +void API_ROUTINE gds__trace(const TEXT * text) +{ +/************************************** + * + * g d s _ t r a c e + * + ************************************** + * + * Functional description + * Post trace event to a log file. + * This function tries to be async-signal safe + * + **************************************/ + + TEXT name[MAXPATHLEN]; + + + // This function is not truly signal safe now. + // It calls string::c_str() and may call getenv(), not good. + // We can only hope that failure is unlikely in it... + gds__prefix(name, LOGFILE); + + time_t now = time((time_t *)0); // is specified in POSIX to be signal-safe + + // 07 Sept 2003, Nickolay Samofatov. + // Since we cannot call ctime/localtime_r or anything else like this from + // signal hanlders we need to decode time by hand. + struct tm today; + + int days, rem; + + days = now / SECS_PER_DAY; + rem = now % SECS_PER_DAY; + today.tm_hour = rem / SECS_PER_HOUR; + rem %= SECS_PER_HOUR; + today.tm_min = rem / 60; + today.tm_sec = rem % 60; + + ndate(days + 40617 /* Number of first day of the Epoch in GDS counting */, + &today); + + char buffer[1024]; // 1K should be enough for the trace message + char *p = buffer; + gds__ulstr(p, today.tm_year+1900, 4, '0'); p+=4; + *p++ = '-'; + gds__ulstr(p, today.tm_mon, 2, '0'); p+=2; + *p++ = '-'; + gds__ulstr(p, today.tm_mday, 2, '0'); p+=2; + *p++ = 'T'; + gds__ulstr(p, today.tm_hour, 2, '0'); p+=2; + *p++ = ':'; + gds__ulstr(p, today.tm_min, 2, '0'); p+=2; + *p++ = ':'; + gds__ulstr(p, today.tm_sec, 2, '0'); p+=2; + *p++ = ' '; + gds__ulstr(p, +#ifdef WIN_NT +#ifdef SUPERSERVER + GetCurrentThreadId(), +#else + GetCurrentProcessId(), +#endif +#else + getpid(), +#endif + 5, ' '); p += 5; + *p++ = ' '; + strcpy(p, text); p += strlen(p); + strcat(p, "\n"); p += strlen(p); +#ifdef WIN_NT + // Signal-unsafe code + IB_FILE *file; + if ((file = ib_fopen(name, FOPEN_APPEND_TYPE)) != NULL) + { + ib_fwrite(buffer, 1, p - buffer, file); + ib_fclose(file); + } +#else + // Note: signal-safe code + int file = open(name, O_CREAT | O_APPEND | O_WRONLY, 0660); + if (file == -1) return; + write(file, buffer, p-buffer); + close(file); +#endif +} + void API_ROUTINE gds__log(const TEXT * text, ...) { /************************************** @@ -1074,8 +1185,8 @@ void API_ROUTINE gds__log(const TEXT * text, ...) if ((file = ib_fopen(name, FOPEN_APPEND_TYPE)) != NULL) { - ib_fprintf(file, "%s%s\t%.25s\t", ISC_get_host(name, MAXPATHLEN), - gdslogid, ctime(&now)); + ib_fprintf(file, "\n%s%s\t%.25s\t", + ISC_get_host(name, MAXPATHLEN), gdslogid, ctime(&now)); VA_START(ptr, text); ib_vfprintf(file, text, ptr); ib_fprintf(file, "\n\n"); diff --git a/src/jrd/gds_proto.h b/src/jrd/gds_proto.h index fbe3ba0fc2..14444ddbe3 100644 --- a/src/jrd/gds_proto.h +++ b/src/jrd/gds_proto.h @@ -87,6 +87,7 @@ ULONG API_ROUTINE gds__free(void*); SLONG API_ROUTINE gds__interprete(char*, ISC_STATUS**); void API_ROUTINE gds__interprete_a(SCHAR*, SSHORT*, ISC_STATUS*, SSHORT*); void API_ROUTINE gds__log(const TEXT*, ...); +void API_ROUTINE gds__trace(const TEXT*); void API_ROUTINE gds__log_status(TEXT*, ISC_STATUS*); int API_ROUTINE gds__msg_close(void*); SSHORT API_ROUTINE gds__msg_format(void* handle, @@ -133,6 +134,7 @@ void API_ROUTINE isc_print_sqlerror(SSHORT, ISC_STATUS*); void API_ROUTINE isc_sql_interprete(SSHORT, TEXT*, SSHORT); SINT64 API_ROUTINE isc_portable_integer(UCHAR*, SSHORT); void gds__cleanup(void); +extern void gds__ulstr(char* buffer, ULONG value, int maxlen, char filler); #if (defined SOLARIS && !defined(MAP_ANON)) diff --git a/src/jrd/isc_i_proto.h b/src/jrd/isc_i_proto.h deleted file mode 100644 index fb5c970088..0000000000 --- a/src/jrd/isc_i_proto.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * PROGRAM: JRD Access Method - * MODULE: isc_i_proto.h - * DESCRIPTION: Prototype header file for isc_ipc.c - * - * The contents of this file are subject to the Interbase Public - * License Version 1.0 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy - * of the License at http://www.Inprise.com/IPL.html - * - * Software distributed under the License is distributed on an - * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express - * or implied. See the License for the specific language governing - * rights and limitations under the License. - * - * The Original Code was created by Inprise Corporation - * and its predecessors. Portions created by Inprise Corporation are - * Copyright (C) Inprise Corporation. - * - * All Rights Reserved. - * Contributor(s): ______________________________________. - */ - -#ifndef _JRD_ISC_I_PROTO_H_ -#define _JRD_ISC_I_PROTO_H_ - -#ifdef __cplusplus -extern "C" { -#endif - -void DLL_EXPORT ISC_enter(void); -void DLL_EXPORT ISC_exit(void); - -#ifdef __cplusplus -} -#endif - -void DLL_EXPORT ISC_enable(void); -void DLL_EXPORT ISC_inhibit(void); - -#ifdef WIN_NT -int API_ROUTINE ISC_kill(SLONG, SLONG, void *); -#else -int ISC_kill(SLONG, SLONG); -#endif - -/* Signal routines have FPTR_VOID parameters instead of SIG_FPTR to - hide OS signal implementation details for this module users. - SIG_FPTR is very platform dependent. C/C++ ignores redundant function - parameters anyway -*/ - -#if (defined __cplusplus) && (defined SOLX86) -/* Who else got mixed c and C++ linkage error - let join me. KLK -*/ -extern "C" { -#endif - -extern void API_ROUTINE ISC_signal(int, FPTR_VOID, void *); -extern void API_ROUTINE ISC_signal_cancel(int, FPTR_VOID, void *); -extern void DLL_EXPORT ISC_signal_init(void); - -#if (defined __cplusplus) && (defined SOLX86) -/* Who else got mixed c and C++ linkage error - let join me. KLK -*/ -} -#endif - -#endif /* _JRD_ISC_I_PROTO_H_ */ diff --git a/src/jrd/isc_ipc.cpp b/src/jrd/isc_ipc.cpp deleted file mode 100644 index ae15ebeadf..0000000000 --- a/src/jrd/isc_ipc.cpp +++ /dev/null @@ -1,1155 +0,0 @@ -/* - * PROGRAM: JRD Access Method - * MODULE: isc_ipc.c - * DESCRIPTION: General purpose but non-user routines. - * - * The contents of this file are subject to the Interbase Public - * License Version 1.0 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy - * of the License at http://www.Inprise.com/IPL.html - * - * Software distributed under the License is distributed on an - * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express - * or implied. See the License for the specific language governing - * rights and limitations under the License. - * - * The Original Code was created by Inprise Corporation - * and its predecessors. Portions created by Inprise Corporation are - * Copyright (C) Inprise Corporation. - * - * All Rights Reserved. - * Contributor(s): ______________________________________. - * Solaris x86 changes - Konstantin Kuznetsov, Neil McCalden - * - * 2002.02.15 Sean Leyne - Code Cleanup, removed obsolete ports: - * - EPSON, DELTA, IMP, NCR3000 and M88K - * - * 2002.10.27 Sean Leyne - Code Cleanup, removed obsolete "UNIXWARE" port - * - * 2002.10.28 Sean Leyne - Completed removal of obsolete "DGUX" port - * - * 2002.10.29 Sean Leyne - Removed obsolete "Netware" port - * - * 2002.10.30 Sean Leyne - Removed support for obsolete "PC_PLATFORM" define - * - */ - - /* $Id: isc_ipc.cpp,v 1.38 2003-08-28 13:15:23 brodsom Exp $ */ - -#ifdef SHLIB_DEFS -#define LOCAL_SHLIB_DEFS -#endif - -#include "firebird.h" -#include "../jrd/ib_stdio.h" -#include -#include "../jrd/common.h" -#include "gen/codes.h" -#include "../jrd/isc.h" -#include "../jrd/gds_proto.h" -#include "../jrd/isc_proto.h" -#include "../jrd/isc_i_proto.h" -#include "../jrd/isc_s_proto.h" -#include "../jrd/thd_proto.h" - -#ifdef HAVE_VFORK_H -#include -#endif - -#ifdef sparc -#ifdef SOLARIS -#define HANDLER_ADDR_ARG -#endif -#endif - -#ifdef HAVE_STRING_H -#include -#endif - -#ifdef SOLX86 -#define HANDLER_ADDR_ARG -#endif - -typedef struct sig { - struct sig *sig_next; - int sig_signal; - SIG_FPTR sig_routine; - void *sig_arg; - SLONG sig_count; - IPTR sig_thread_id; - USHORT sig_flags; -} *SIG; - -#define SIG_client 1 /* Not our routine */ -#define SIG_informs 2 /* routine tells us whether to chain */ - -#define SIG_informs_continue 0 /* continue on signal processing */ -#define SIG_informs_stop 1 /* stop signal processing */ - - -#if (defined AIX || defined AIX_PPC) -#define GT_32_SIGNALS -#endif - -#ifdef SCO_EV -#define HANDLER_ADDR_ARG -#endif - -#ifndef REQUESTER -static USHORT initialized_signals = FALSE; -static SIG volatile signals = NULL; -static USHORT volatile inhibit_count = 0; -static SLONG volatile overflow_count = 0; - -#ifdef MULTI_THREAD -static MUTX_T sig_mutex; -#endif - -#ifdef GT_32_SIGNALS -static SLONG volatile pending_signals[2]; -#else -static SLONG volatile pending_signals = 0; -#endif -static int process_id = 0; - -#endif /* of ifndef REQUESTER */ - - -/* VMS Specific Stuff */ - -#ifdef VMS -#include -#endif - - -/* Unix specific stuff */ - -#ifdef UNIX -#include -#include -#include - -#ifdef HAVE_SIGNAL_H -#include -#endif - -#ifdef HAVE_SYS_SIGNAL_H -#include -#endif - -#include -#ifdef HAVE_UNISTD_H -#include -#endif - -#ifndef O_RDWR -#include -#endif - -#define LOCAL_SEMAPHORES 4 - -#ifdef SYSV_SIGNALS -#define SIGVEC FPTR_INT -#endif - -#ifdef HAVE_SIGACTION -#define SIGVEC struct sigaction -#endif - -#ifndef GDS_RELAY -#define GDS_RELAY "/bin/gds_relay" -#endif - -#ifndef SHMEM_DELTA -#define SHMEM_DELTA (1 << 22) -#endif - -static int volatile relay_pipe = 0; -#endif - - -/* Windows NT */ - -#ifdef WIN_NT - -#include -#include -#include - -#ifdef TEXT -#undef TEXT -#endif - -#define TEXT SCHAR - -#ifndef NSIG -#define NSIG 100 -#endif - -#define SIGVEC SIG_FPTR - -#define MAX_OPN_EVENTS 40 - -typedef struct opn_event { - SLONG opn_event_pid; - SLONG opn_event_signal; /* pseudo-signal number */ - HANDLE opn_event_lhandle; /* local handle to foreign event */ - ULONG opn_event_age; -} *OPN_EVENT; - -static struct opn_event opn_events[MAX_OPN_EVENTS]; -static USHORT opn_event_count; -static ULONG opn_event_clock; -#endif - -#if (defined __cplusplus) && (defined SOLX86) -/* Who else got mixed c and C++ linkage error - let join me. KLK -*/ -extern "C" { -#endif - -static void cleanup(void *); -#ifdef NOT_USED_OR_REPLACED -#ifndef REQUESTER -static void error(ISC_STATUS *, TEXT *, ISC_STATUS); -#endif -#endif -static void isc_signal2(int, SIG_FPTR, void *, ULONG); -static SLONG overflow_handler(void *); -static SIG que_signal(int, SIG_FPTR, void *, int); - -#if !(defined HANDLER_ADDR_ARG) -#ifdef SINIXZ -static void CLIB_ROUTINE signal_handler(int, int); -#else -static void CLIB_ROUTINE signal_handler(int, int, struct sigcontext *); -#endif -#endif - -#ifdef HANDLER_ADDR_ARG -static void CLIB_ROUTINE signal_handler(int, int, void *, void *); -#endif - -#ifdef OLD_POSIX_THREADS -static void sigwait_thread(int); -#endif - -#ifndef sigvector -#ifndef hpux -#define sigvector sigvec -#endif -#endif - -#ifndef SIGVEC -#define SIGVEC struct sigvec -#endif - -#ifndef SIG_HOLD -#define SIG_HOLD SIG_DFL -#endif - -// Not thread-safe - -extern "C" { - ULONG isc_enter_count = 0; -} - -//static SIGVEC client_sigfpe; - -#ifdef SHLIB_DEFS -#define sprintf (*_libgds_sprintf) -#define strlen (*_libgds_strlen) -#define strcpy (*_libgds_strcpy) -#define exit (*_libgds_exit) -#define _iob (*_libgds__iob) -#define getpid (*_libgds_getpid) -#define errno (*_libgds_errno) -#define kill (*_libgds_kill) -#define _exit (*_libgds__exit) -#define pipe (*_libgds_pipe) -#define fork (*_libgds_fork) -#define write (*_libgds_write) -#define _ctype (*_libgds__ctype) -#define sigvector (*_libgds_sigvec) -#define execl (*_libgds_execl) -#define sigset (*_libgds_sigset) -#define ib_fprintf (*_libgds_fprintf) -#define close (*_libgds_close) - -extern int sprintf(); -extern int strlen(); -extern SCHAR *strcpy(); -extern void exit(); -extern IB_FILE _iob[]; -extern pid_t getpid(); -extern int errno; -extern int kill(); -extern void _exit(); -extern int pipe(); -extern pid_t fork(); -extern int write(); -extern SCHAR _ctype[]; -extern int sigvector(); -extern int execl(); -extern void (*sigset()) (); -extern int ib_fprintf(); -extern int close(); -#endif // SHLIB_DEFS - - -#if (defined __cplusplus) && (defined SOLX86) -} -#endif - -extern "C" { - -void DLL_EXPORT ISC_enter(void) -{ -/************************************** - * - * I S C _ e n t e r - * - ************************************** - * - * Functional description - * Enter ISC world from caller. - * - **************************************/ -/* Cancel our handler for SIGFPE - in case it was already there */ - ISC_signal_cancel(SIGFPE, (FPTR_VOID) overflow_handler, NULL); - -/* Setup overflow handler - with chaining to any user handler */ - isc_signal2(SIGFPE, (SIG_FPTR) overflow_handler, NULL, SIG_informs); - -#ifdef DEBUG_FPE_HANDLING -/* Debug code to simulate an FPE occuring during DB Operation */ - if (overflow_count < 100) - kill(getpid(), SIGFPE); -#endif -} - -} // extern "C" - - -#ifndef REQUESTER -void DLL_EXPORT ISC_enable(void) -{ -/************************************** - * - * I S C _ e n a b l e - * - ************************************** - * - * Functional description - * Enable signal processing. Re-post any pending signals. - * - **************************************/ - -#if defined(UNIX) - USHORT n; -#endif - -#ifdef GT_32_SIGNALS - SLONG p; - USHORT i; -#endif - - if (inhibit_count) - --inhibit_count; - - if (inhibit_count) - return; - -#ifdef UNIX -#ifdef GT_32_SIGNALS - while (pending_signals[0] || pending_signals[1]) - for (i = 0; i < 2; i++) { - for (n = 0, p = pending_signals[i]; p && n < 32; n++) - if (p & (1 << n)) { - p &= ~(1 << n); - ISC_kill(process_id, n + 1 + i * 32); - } - /* This looks like a danger point - if one of the bits - * was reset after we sent the signal then we will lose it. - */ - pending_signals[i] = 0; - } -#else - while (pending_signals) - for (n = 0; pending_signals && n < 32; n++) - if (pending_signals & (1 << n)) { - pending_signals &= ~(1 << n); - ISC_kill(process_id, n + 1); - } -#endif -#endif - -} -#endif - - -extern "C" { -void DLL_EXPORT ISC_exit(void) -{ -/************************************** - * - * I S C _ e x i t - * - ************************************** - * - * Functional description - * Exit ISC world, return to caller. - * - **************************************/ - -/* No longer attempt to handle overflow internally */ - ISC_signal_cancel(SIGFPE, (FPTR_VOID) overflow_handler, 0); -} -} // Extern "C" - - -#ifndef REQUESTER -void DLL_EXPORT ISC_inhibit(void) -{ -/************************************** - * - * I S C _ i n h i b i t - * - ************************************** - * - * Functional description - * Inhibit process of signals. Signals will be - * retained until signals are eventually re-enabled, - * then re-posted. - * - **************************************/ - - ++inhibit_count; -} -#endif - - -#if (defined VMS && defined __ALPHA) -int ISC_kill(SLONG pid, SLONG signal_number) -{ -/************************************** - * - * I S C _ k i l l ( A p o l l o & A l p h a / O p e n V M S ) - * - ************************************** - * - * Functional description - * Notify somebody else. - * - **************************************/ - - return kill(pid, signal_number); -} -#endif - - -#ifdef UNIX -int ISC_kill(SLONG pid, SLONG signal_number) -{ -/************************************** - * - * I S C _ k i l l ( U N I X ) - * - ************************************** - * - * Functional description - * Notify somebody else. - * - **************************************/ - SLONG msg[3]; - int status, pipes[2]; - TEXT process[MAXPATHLEN], arg[10]; - - for (;;) { - status = kill(pid, signal_number); - - if (!status) - return status; - if (SYSCALL_INTERRUPTED(errno)) - continue; - if (errno == EPERM) - break; - - return status; - } - -/* Process is there, but we don't have the privilege to - send to him. */ - - if (!relay_pipe) { - gds__prefix(process, GDS_RELAY); - if (pipe(pipes)) { - gds__log("ISC_kill: error %d creating gds_relay", errno); - return -1; - } - sprintf(arg, "%d", pipes[0]); - if (!vfork()) { - execl(process, process, arg, 0); - gds__log("ISC_kill: error %d starting gds_relay %s", errno, - process); - _exit(0); - } - relay_pipe = pipes[1]; - - /* Don't need the READ pipe */ - close(pipes[0]); - } - - msg[0] = pid; - msg[1] = signal_number; - msg[2] = msg[0] ^ msg[1]; /* XOR for a consistancy check */ - if (write(relay_pipe, msg, sizeof(msg)) != sizeof(msg)) { - gds__log("ISC_kill: write to relay_pipe failed %d", errno); - relay_pipe = 0; /* try to restart next time */ - return -1; - } - - return 0; -} -#endif - - -#ifdef WIN_NT -int API_ROUTINE ISC_kill(SLONG pid, SLONG signal_number, void *object_hndl) -{ -/************************************** - * - * I S C _ k i l l ( W I N _ N T ) - * - ************************************** - * - * Functional description - * Notify somebody else. - * - **************************************/ - ULONG oldest_age; - OPN_EVENT opn_event, end_opn_event, oldest_opn_event; - -/* If we're simply trying to poke ourselves, do so directly. */ - if (!process_id) - process_id = GetCurrentProcessId(); - - if (pid == process_id) { - SetEvent(object_hndl); - return 0; - } - - oldest_age = ~0; - - opn_event = opn_events; - end_opn_event = opn_event + opn_event_count; - for (; opn_event < end_opn_event; opn_event++) { - if (opn_event->opn_event_pid == pid && - opn_event->opn_event_signal == signal_number) break; - if (opn_event->opn_event_age < oldest_age) { - oldest_opn_event = opn_event; - oldest_age = opn_event->opn_event_age; - } - } - - if (opn_event >= end_opn_event) { - HANDLE lhandle; - - if (!(lhandle = ISC_make_signal(FALSE, FALSE, pid, signal_number))) - return -1; - - if (opn_event_count < MAX_OPN_EVENTS) - opn_event_count++; - else { - opn_event = oldest_opn_event; - CloseHandle(opn_event->opn_event_lhandle); - } - - opn_event->opn_event_pid = pid; - opn_event->opn_event_signal = signal_number; - opn_event->opn_event_lhandle = lhandle; - } - - opn_event->opn_event_age = ++opn_event_clock; - - return (SetEvent(opn_event->opn_event_lhandle)) ? 0 : -1; -} -#endif - -#if (defined __cplusplus) && (defined SOLX86) -/* Who else got mixed c and C++ linkage error - let join me. KLK -*/ -extern "C" { -#endif - -void API_ROUTINE ISC_signal(int signal_number, FPTR_VOID handler, void *arg) -{ -/************************************** - * - * I S C _ s i g n a l - * - ************************************** - * - * Functional description - * Multiplex multiple handers into single signal. - * - **************************************/ - isc_signal2(signal_number, (SIG_FPTR)handler, arg, 0); -} - - -#ifdef SYSV_SIGNALS -static void isc_signal2( - int signal_number, - SIG_FPTR handler, void *arg, ULONG flags) -{ -/************************************** - * - * i s c _ s i g n a l 2 ( S Y S V _ S I G N A L S ) - * - ************************************** - * - * Functional description - * Multiplex multiple handers into single signal. - * - **************************************/ - SIG sig; - int n; - FPTR_INT ptr; - -/* The signal handler needs the process id */ - - if (!process_id) - process_id = getpid(); - - THD_MUTEX_LOCK(&sig_mutex); - -/* See if this signal has ever been cared about before */ - - for (sig = signals; sig; sig = sig->sig_next) - if (sig->sig_signal == signal_number) - break; - -/* If it hasn't been attach our chain handler to the signal, - and queue up whatever used to handle it as a non-ISC - routine (they are invoked differently). Note that if - the old action was SIG_DFL, SIG_HOLD, SIG_IGN or our - multiplexor, there is no need to save it. */ - - if (!sig) { - ptr = sigset(signal_number, signal_handler); - if (ptr != SIG_DFL && - ptr != SIG_IGN && - ptr != SIG_HOLD && - ptr != signal_handler) - que_signal(signal_number, ptr, arg, SIG_client); - } - -/* Que up the new ISC signal handler routine */ - - que_signal(signal_number, handler, arg, flags); - - THD_MUTEX_UNLOCK(&sig_mutex); -} -#endif /* SYSV */ - - -#if (defined UNIX || defined WIN_NT) -#ifndef SYSV_SIGNALS -static void isc_signal2( - int signal_number, - SIG_FPTR handler, void *arg, ULONG flags) -{ -/************************************** - * - * i s c _ s i g n a l 2 ( u n i x , W I N _ N T , O S 2 ) - * - ************************************** - * - * Functional description - * Multiplex multiple handers into single signal. - * - **************************************/ - - SIG sig; - SIG_FPTR ptr; - -/* The signal handler needs the process id */ - if (!process_id) - process_id = getpid(); - -#if defined(WIN_NT) -/* If not a UNIX signal, just queue for port watcher. */ - - if (signal_number > NSIG) { - que_signal(signal_number, handler, arg, flags); - return; - } -#endif - - THD_MUTEX_LOCK(&sig_mutex); - -/* See if this signal has ever been cared about before */ - - for (sig = signals; sig; sig = sig->sig_next) - if (sig->sig_signal == signal_number) - break; - -/* If it hasn't been attach our chain handler to the signal, - and queue up whatever used to handle it as a non-ISC - routine (they are invoked differently). Note that if - the old action was SIG_DFL, SIG_HOLD, SIG_IGN or our - multiplexor, there is no need to save it. */ - - if (!sig) { -#if (defined WIN_NT) - ptr = (SIG_FPTR) signal(signal_number, (SIG_FPTR) signal_handler); -#else - SIGVEC vec, old_vec; - -#ifndef HAVE_SIGACTION -#ifdef OLD_POSIX_THREADS - if (signal_number != SIGALRM) - vec.sv_handler = SIG_DFL; - else -#endif - vec.sv_handler = (SIG_FPTR) signal_handler; - vec.sv_mask = 0; - vec.sv_onstack = 0; - sigvector(signal_number, &vec, &old_vec); - ptr = old_vec.sv_handler; -#else -#ifdef OLD_POSIX_THREADS - if (signal_number != SIGALRM) - vec.sv_handler = SIG_DFL; - else -#endif - vec.sa_handler = (SIG_FPTR) signal_handler; - memset(&vec.sa_mask, 0, sizeof(vec.sa_mask)); - vec.sa_flags = SA_RESTART; - sigaction(signal_number, &vec, &old_vec); - ptr = (SIG_FPTR) old_vec.sa_handler; -#endif -#endif - if (ptr != (SIG_FPTR) SIG_DFL && - ptr != (SIG_FPTR) SIG_HOLD && - ptr != (SIG_FPTR) SIG_IGN && ptr != (SIG_FPTR) signal_handler) { - que_signal(signal_number, (SIG_FPTR) ptr, arg, SIG_client); - } - } - - /* Que up the new ISC signal handler routine */ - - que_signal(signal_number, handler, arg, flags); - - THD_MUTEX_UNLOCK(&sig_mutex); -} -#endif -#endif - - -#ifndef REQUESTER -void API_ROUTINE ISC_signal_cancel( - int signal_number, - FPTR_VOID handler, void *arg) -{ -/************************************** - * - * I S C _ s i g n a l _ c a n c e l - * - ************************************** - * - * Functional description - * Cancel a signal handler. - * If handler == NULL, cancel all handlers for a given signal. - * - **************************************/ - SIG sig; - volatile SIG *ptr; - - THD_MUTEX_LOCK(&sig_mutex); - - for (ptr = &signals; sig = *ptr;) { - if (sig->sig_signal == signal_number && - (handler == NULL || - ((FPTR_VOID)sig->sig_routine == handler && sig->sig_arg == arg))) { - *ptr = sig->sig_next; - gds__free(sig); - } - else - ptr = &(*ptr)->sig_next; - } - - THD_MUTEX_UNLOCK(&sig_mutex); - -#ifdef OLD_POSIX_THREADS - { - IPTR thread_id; - - /* UNSAFE CODE HERE - sig has been freed - rewrite should - * this section ever be activated. - */ - deliberate_error_here_to_force_compile_error++; - if (!sig || signal_number == SIGALRM) - return; - - thread_id = sig->sig_thread_id; - for (sig = signals; sig; sig = sig->sig_next) - if (sig->sig_signal == signal_number) - return; - - /* No more handlers exist for the signal. Kill the thread that's - been listening for the signal. */ - - pthread_cancel((pthread_t *) thread_id); - } -#endif // OLD_POSIX_THREADS -} -#endif // ifndef REQUESTER - - -void DLL_EXPORT ISC_signal_init(void) -{ -/************************************** - * - * I S C _ s i g n a l _ i n i t - * - ************************************** - * - * Functional description - * Initialize any system signal handlers. - * - **************************************/ - -#ifndef REQUESTER - if (initialized_signals) - return; - - initialized_signals = TRUE; - - overflow_count = 0; - gds__register_cleanup(cleanup, 0); - -#ifndef VMS - process_id = getpid(); - - THD_MUTEX_INIT(&sig_mutex); - - isc_signal2(SIGFPE, (SIG_FPTR) overflow_handler, 0, SIG_informs); -#endif - -#endif /* REQUESTER */ - -#ifdef WIN_NT - ISC_get_security_desc(); -#endif -} - - -#ifndef REQUESTER -static void cleanup(void *arg) -{ -/************************************** - * - * c l e a n u p - * - ************************************** - * - * Functional description - * Module level cleanup handler. - * - **************************************/ - signals = NULL; - - THD_MUTEX_DESTROY(&sig_mutex); - - inhibit_count = 0; - -#ifdef GT_32_SIGNALS - pending_signals[0] = pending_signals[1] = 0; -#else - pending_signals = 0; -#endif - - process_id = 0; - -#ifdef WIN_NT - { - OPN_EVENT opn_event; - - opn_event = opn_events + opn_event_count; - opn_event_count = 0; - while (opn_event-- > opn_events) - CloseHandle(opn_event->opn_event_lhandle); - } -#endif - - initialized_signals = FALSE; -} -#endif - -#ifdef NOT_USED_OR_REPLACED -#ifndef REQUESTER -static void error(ISC_STATUS * status_vector, TEXT * string, ISC_STATUS status) -{ -/************************************** - * - * e r r o r - * - ************************************** - * - * Functional description - * We've encountered an error, report it. - * - **************************************/ - - *status_vector++ = gds_arg_gds; - *status_vector++ = gds_sys_request; - *status_vector++ = gds_arg_string; - *status_vector++ = (ISC_STATUS) string; - *status_vector++ = SYS_ARG; - *status_vector++ = status; - *status_vector++ = gds_arg_end; -} -#endif -#endif - -#ifndef REQUESTER -static SLONG overflow_handler(void *arg) -{ -/************************************** - * - * o v e r f l o w _ h a n d l e r - * - ************************************** - * - * Functional description - * Somebody overflowed. Ho hum. - * - **************************************/ - -#ifdef DEBUG_FPE_HANDLING - ib_fprintf(ib_stderr, "overflow_handler (%x)\n", arg); -#endif - -/* If we're within ISC world (inside why-value) when the FPE occurs - * we handle it (basically by ignoring it). If it occurs outside of - * ISC world, return back a code that tells signal_handler to call any - * customer provided handler. - */ - if (isc_enter_count) { - ++overflow_count; -#ifdef DEBUG_FPE_HANDLING - ib_fprintf(ib_stderr, "SIGFPE in isc code ignored %d\n", - overflow_count); -#endif - /* We've "handled" the FPE - let signal_handler know not to chain - the signal to other handlers */ - return SIG_informs_stop; - } - else { - /* We've NOT "handled" the FPE - let signal_handler know to chain - the signal to other handlers */ - return SIG_informs_continue; - } -} -#endif - - -#ifndef REQUESTER -static SIG que_signal( - int signal_number, - SIG_FPTR handler, void *arg, int flags) -{ -/************************************** - * - * q u e _ s i g n a l - * - ************************************** - * - * Functional description - * Que signal for later action. - * - **************************************/ - SIG sig; - IPTR thread_id = 0; - -#ifdef OLD_POSIX_THREADS - if (signal_number != SIGALRM) { - for (sig = signals; sig; sig = sig->sig_next) - if (sig->sig_signal == signal_number) - break; - - if (!sig) - pthread_create((pthread_t *) & thread_id, pthread_attr_default, - sigwait_thread, (void *) signal_number); - else - thread_id = sig->sig_thread_id; - } -#endif - - sig = (SIG) gds__alloc((SLONG) sizeof(struct sig)); -/* FREE: unknown */ - if (!sig) { /* NOMEM: */ - DEV_REPORT("que_signal: out of memory"); - return NULL; /* NOMEM: not handled, too difficult */ - } - -#ifdef DEBUG_GDS_ALLOC -/* This will only be freed when a signal handler is de-registered - * and we don't do that at process exit - so this not always - * a freed structure. - */ - gds_alloc_flag_unfreed((void *) sig); -#endif - - sig->sig_signal = signal_number; - sig->sig_routine = handler; - sig->sig_arg = arg; - sig->sig_flags = flags; - sig->sig_thread_id = thread_id; - sig->sig_count = 0; - - sig->sig_next = signals; - signals = sig; - - return sig; -} -#endif - - -#ifndef REQUESTER -static void CLIB_ROUTINE signal_handler(int number, -#ifdef SINIXZ - int code) -#else - int code, -#ifdef HANDLER_ADDR_ARG - void *scp, void *addr) -#else - struct sigcontext *scp) -#endif -#endif /* SINIXZ */ -{ -/************************************** - * - * s i g n a l _ h a n d l e r ( G E N E R I C ) - * - ************************************** - * - * Functional description - * Checkin with various signal handlers. - * - **************************************/ - SIG sig; - -/* if there are no signals, who cares? */ - - if (signals == NULL) - return; - -/* This should never happen, but if it does might as well not crash */ - - if (number == 0) - return; - -/* If signals are inhibited, save the signal for later reposting. - Otherwise, invoke everybody who may have expressed an interest. */ - -#ifdef SIGALRM - if (inhibit_count && number != SIGALRM) -#else - if (inhibit_count) -#endif -#ifdef GT_32_SIGNALS - pending_signals[(number - 1) / 32] |= 1L << ((number - 1) % 32); -#else - pending_signals |= 1L << (number - 1); -#endif - else - for (sig = signals; sig; sig = sig->sig_next) - if (sig->sig_signal == number) - if (sig->sig_flags & SIG_client) -#pragma FB_COMPILER_MESSAGE("Fix! Ugly function pointer cast!") -#ifdef HANDLER_ADDR_ARG - ((void (*)(...)) (*sig->sig_routine)) (number, code, scp, addr); -#elif defined(SINIXZ) - ((void (*)(...)) (*sig->sig_routine)) (number, code); -#else - ((void (*)(...)) (*sig->sig_routine)) (number, code, scp); -#endif - else if (sig->sig_flags & SIG_informs) { - ULONG res; - /* Routine will tell us whether to chain the signal to other handlers */ -#pragma FB_COMPILER_MESSAGE("Fix! Ugly function pointer cast!") - res = ((*((SLONG(*)(void *)) sig->sig_routine)) - (sig->sig_arg)); - if (res == SIG_informs_stop) - break; - } - else - ((FPTR_VOID_PTR) (*sig->sig_routine)) (sig->sig_arg); - -#ifdef SIG_RESTART - scp->sc_syscall_action = (!ISC_check_restart() - || number == - SIGALRM) ? SIG_RETURN : SIG_RESTART; -#endif -} -#endif - - -#ifdef OLD_POSIX_THREADS -static void sigwait_thread(int signal_number) -{ -/************************************** - * - * s i g w a i t _ t h r e a d - * - ************************************** - * - * Functional description - * This thread waits for a given signal - * and calls the all purpose signal - * handler whenever it arrives. - * - **************************************/ - sigset_t sigmask; - SIG sig; - - sigemptyset(&sigmask); - sigaddset(&sigmask, signal_number); - - while (TRUE) { - sigwait(&sigmask); - - /* If signals are inhibited, save the signal for later reposting. - Otherwise, invoke everybody who may have expressed an interest. */ - - if (inhibit_count && signal_number != SIGALRM) -#ifdef GT_32_SIGNALS - pending_signals[(signal_number - 1) / 32] |= - 1 << ((signal_number - 1) % 32); -#else - pending_signals |= 1 << (signal_number - 1); -#endif - else - for (sig = signals; sig; sig = sig->sig_next) - if (sig->sig_signal == signal_number) - if (sig->sig_flags & SIG_client) -#ifdef HANDLER_ADDR_ARG - (*sig->sig_routine) (number, 0, NULL, NULL); -#else - (*sig->sig_routine) (number, 0, NULL); -#endif - else - (*sig->sig_routine) (sig->sig_arg); - } -} -#endif - -#if (defined __cplusplus) && (defined SOLX86) -/* Who else got mixed c and C++ linkage error - let join me. KLK -*/ -} -#endif diff --git a/src/jrd/isc_s_proto.h b/src/jrd/isc_s_proto.h index 39e678fc30..f8487df56e 100644 --- a/src/jrd/isc_s_proto.h +++ b/src/jrd/isc_s_proto.h @@ -49,7 +49,7 @@ extern int ISC_event_init_shared(struct event *, extern int DLL_EXPORT ISC_event_post(struct event *); extern int DLL_EXPORT ISC_event_wait(SSHORT, struct event **, - SLONG *, SLONG, FPTR_VOID, void *); + SLONG *, SLONG, FPTR_VOID_PTR, void *); #ifdef WIN_NT extern void *ISC_make_signal(BOOLEAN, BOOLEAN, int, int); @@ -89,8 +89,8 @@ extern ULONG ISC_exception_post(ULONG, TEXT *); extern UCHAR *DLL_EXPORT ISC_remap_file(ISC_STATUS *, struct sh_mem *, SLONG, USHORT); -extern void ISC_reset_timer(FPTR_VOID, void *, SLONG *, void **); -extern void ISC_set_timer(SLONG, FPTR_VOID, void *, SLONG *, void **); +extern void ISC_reset_timer(FPTR_VOID_PTR, void *, SLONG *, void **); +extern void ISC_set_timer(SLONG, FPTR_VOID_PTR, void *, SLONG *, void **); extern void DLL_EXPORT ISC_unmap_file(ISC_STATUS *, struct sh_mem *, USHORT); diff --git a/src/jrd/isc_sync.cpp b/src/jrd/isc_sync.cpp index a8507c3d51..46ac462e56 100644 --- a/src/jrd/isc_sync.cpp +++ b/src/jrd/isc_sync.cpp @@ -67,7 +67,7 @@ #include "../jrd/isc.h" #include "../jrd/gds_proto.h" #include "../jrd/isc_proto.h" -#include "../jrd/isc_i_proto.h" +#include "../jrd/os/isc_i_proto.h" #include "../jrd/isc_s_proto.h" #include "../jrd/file_params.h" #include "../jrd/gdsassert.h" @@ -187,7 +187,7 @@ static void error(ISC_STATUS *, TEXT *, ISC_STATUS); static SLONG find_key(ISC_STATUS *, TEXT *); #endif #if defined(UNIX) && !defined(POSIX_THREADS) && !defined(SOLARIS_MT) -static void alarm_handler(void); +static void alarm_handler(void* arg); static SLONG open_semaphores(ISC_STATUS *, SLONG, int&); static SLONG create_semaphores(ISC_STATUS *, SLONG, int); static BOOLEAN semaphore_wait_isc_sync(int, int, int *); @@ -454,7 +454,7 @@ int ISC_event_wait(SSHORT count, EVENT* events, SLONG* values, SLONG micro_seconds, - FPTR_VOID timeout_handler, + FPTR_VOID_PTR timeout_handler, void* handler_arg) { /************************************** @@ -722,7 +722,7 @@ int ISC_event_wait( EVENT * events, SLONG * values, SLONG micro_seconds, - FPTR_VOID timeout_handler, void *handler_arg) + FPTR_VOID_PTR timeout_handler, void *handler_arg) { /************************************** * @@ -964,7 +964,7 @@ int ISC_event_wait( EVENT * events, SLONG * values, SLONG micro_seconds, - FPTR_VOID timeout_handler, void *handler_arg) + FPTR_VOID_PTR timeout_handler, void *handler_arg) { /************************************** * @@ -986,17 +986,8 @@ int ISC_event_wait( EVENT *event; int semid, i; int semnums[16], *semnum; -#ifdef SYSV_SIGNALS - SLONG user_timer; - void *user_handler; -#else struct itimerval user_timer; -#ifndef HAVE_SIGACTION - struct sigvec user_handler; -#else struct sigaction user_handler; -#endif -#endif /* If we're not blocked, the rest is a gross waste of time */ @@ -1170,7 +1161,7 @@ int ISC_event_wait( EVENT * events, SLONG * values, SLONG micro_seconds, - void (*timeout_handler) (), void *handler_arg) + FPTR_VOID_PTR timeout_handler, void *handler_arg) { /************************************** * @@ -1371,7 +1362,7 @@ int DLL_EXPORT ISC_event_wait( EVENT * events, SLONG * values, SLONG micro_seconds, - void (*timeout_handler) (), + FPTR_VOID_PTR timeout_handler, void *handler_arg) { /************************************** @@ -1499,7 +1490,8 @@ int DLL_EXPORT ISC_event_wait( EVENT * events, SLONG * values, SLONG micro_seconds, -void (*timeout_handler) (), void *handler_arg) + FPTR_VOID_PTR timeout_handler, + void *handler_arg) { /************************************** * @@ -3803,7 +3795,7 @@ UCHAR *DLL_EXPORT ISC_remap_file(ISC_STATUS * status_vector, #if (defined UNIX) void ISC_reset_timer( - FPTR_VOID timeout_handler, + FPTR_VOID_PTR timeout_handler, void *timeout_arg, SLONG * client_timer, void **client_handler) { @@ -3818,29 +3810,17 @@ void ISC_reset_timer( * the previous context. * **************************************/ -#ifndef SYSV_SIGNALS struct itimerval internal_timer; -#endif ISC_signal_cancel(SIGALRM, timeout_handler, timeout_arg); /* Cancel the timer, then restore the previous handler and alarm */ -#ifdef SYSV_SIGNALS - alarm(0); - sigset(SIGALRM, *client_handler); - alarm(*client_timer); -#else timerclear(&internal_timer.it_interval); timerclear(&internal_timer.it_value); setitimer(ITIMER_REAL, &internal_timer, NULL); -#ifndef HAVE_SIGACTION - sigvector(SIGALRM, client_handler, NULL); -#else sigaction(SIGALRM, (struct sigaction*)client_handler, NULL); -#endif setitimer(ITIMER_REAL, (itimerval*)client_timer, NULL); -#endif } #endif @@ -3848,7 +3828,7 @@ void ISC_reset_timer( #if (defined UNIX) void ISC_set_timer( SLONG micro_seconds, - FPTR_VOID timeout_handler, + FPTR_VOID_PTR timeout_handler, void *timeout_arg, SLONG * client_timer, void **client_handler) { @@ -3862,51 +3842,23 @@ void ISC_set_timer( * Set a timer for the specified amount of time. * **************************************/ -#ifdef SYSV_SIGNALS - void *d2; - SLONG d1; -#else struct itimerval internal_timer; -#ifndef HAVE_SIGACTION - struct sigvec internal_handler; -#else struct sigaction internal_handler; -#endif -#endif /* Start by cancelling any existing timer */ -#ifdef SYSV_SIGNALS - if (!client_timer) - client_timer = &d1; - *client_timer = alarm(0); -#else timerclear(&internal_timer.it_interval); timerclear(&internal_timer.it_value); setitimer(ITIMER_REAL, &internal_timer, (struct itimerval *) client_timer); -#endif /* Now clear the signal handler while saving the existing one */ -#ifdef SYSV_SIGNALS - if (!client_handler) - client_handler = &d2; - *client_handler = (void *) sigset(SIGALRM, SIG_DFL); -#else -#ifndef HAVE_SIGACTION - internal_handler.sv_handler = SIG_DFL; - internal_handler.sv_mask = 0; - internal_handler.sv_flags = SV_INTERRUPT; - sigvector(SIGALRM, &internal_handler, (struct sigvec *) client_handler); -#else - internal_handler.sa_handler = (SIG_FPTR) SIG_DFL; - memset(&internal_handler.sa_mask, 0, sizeof(internal_handler.sa_mask)); + internal_handler.sa_handler = SIG_DFL; + sigemptyset(&internal_handler.sa_mask); internal_handler.sa_flags = SA_RESTART; sigaction(SIGALRM, &internal_handler, (struct sigaction *) client_handler); -#endif -#endif if (!micro_seconds) return; @@ -3915,12 +3867,6 @@ void ISC_set_timer( ISC_signal(SIGALRM, timeout_handler, timeout_arg); -#ifdef SYSV_SIGNALS - if (micro_seconds < 1000000) - alarm(1); - else - alarm(micro_seconds / 1000000); -#else if (micro_seconds < 1000000) internal_timer.it_value.tv_usec = micro_seconds; else { @@ -3928,7 +3874,6 @@ void ISC_set_timer( internal_timer.it_value.tv_usec = micro_seconds % 1000000; } setitimer(ITIMER_REAL, &internal_timer, NULL); -#endif } #endif @@ -4098,7 +4043,7 @@ void DLL_EXPORT ISC_unmap_file( #if defined(UNIX) && !defined(POSIX_THREADS) && !defined(SOLARIS_MT) -static void alarm_handler(void) +static void alarm_handler(void* arg) { /************************************** * diff --git a/src/jrd/lck.cpp b/src/jrd/lck.cpp index e64ac11678..0e7403e4d7 100644 --- a/src/jrd/lck.cpp +++ b/src/jrd/lck.cpp @@ -191,6 +191,13 @@ JMB: As part of the c++ conversion I removed the check for lck block type. #define LCK_CHECK_LOCK(x) (TRUE) /* nothing */ #endif +void LCK_ast_inhibit() { + LOCK_ast_inhibit(); +} + +void LCK_ast_enable() { + LOCK_ast_enable(); +} void LCK_assert(TDBB tdbb, LCK lock) { @@ -495,6 +502,7 @@ SLONG LCK_get_owner_handle(TDBB tdbb, enum lck_t lock_type) case LCK_range_relation: case LCK_backup_state: case LCK_backup_alloc: + case LCK_backup_database: return LCK_OWNER_HANDLE_DBB; case LCK_attachment: case LCK_relation: diff --git a/src/jrd/lck.h b/src/jrd/lck.h index c99a95fd32..69a7e1d5ac 100644 --- a/src/jrd/lck.h +++ b/src/jrd/lck.h @@ -44,8 +44,9 @@ enum lck_t { LCK_prc_exist, /* Relation existence lock */ LCK_range_relation, /* Relation refresh range lock */ LCK_update_shadow, /* shadow update sync lock */ - LCK_backup_state, /* Lock to synchronize for objects depending on backup status*/ - LCK_backup_alloc /* Lock for page allocation table in backup spare file */ + LCK_backup_state, /* Lock to synchronize for objects depending on backup status */ + LCK_backup_alloc, /* Lock for page allocation table in backup spare file */ + LCK_backup_database /* Lock to protect writing to database file */ }; /* Lock owner types */ diff --git a/src/jrd/lck_proto.h b/src/jrd/lck_proto.h index 4e27b0a6c2..31102af8eb 100644 --- a/src/jrd/lck_proto.h +++ b/src/jrd/lck_proto.h @@ -46,6 +46,8 @@ extern SLONG LCK_read_data(struct lck *); extern void LCK_release(TDBB, struct lck *); extern void LCK_re_post(struct lck *); extern void LCK_write_data(struct lck *, SLONG); +extern void LCK_ast_inhibit(); +extern void LCK_ast_enable(); #ifdef __cplusplus } /* extern "C" */ diff --git a/src/jrd/nbak.cpp b/src/jrd/nbak.cpp index 58a9dc3e92..37e6820205 100644 --- a/src/jrd/nbak.cpp +++ b/src/jrd/nbak.cpp @@ -3,24 +3,36 @@ * MODULE: nbak.cpp * DESCRIPTION: New backup technology * - * The contents of this file are subject to the Interbase Public - * License Version 1.0 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy - * of the License at http://www.Inprise.com/IPL.html + * This library 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; either + * version 2.1 of the License, or (at your option) any later version. + * You may obtain a copy of the Licence at + * http://www.gnu.org/licences/lgpl.html + * + * As a special exception this file can also be included in modules + * with other source code as long as that source code has been + * released under an Open Source Initiative certificed licence. + * More information about OSI certification can be found at: + * http://www.opensource.org + * + * This module 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 Licence for more details. + * + * This module was created by members of the firebird development + * team. All individual contributions remain the Copyright (C) of + * those individuals and all rights are reserved. Contributors to + * this file are either listed below or can be obtained from a CVS + * history command. * - * Software distributed under the License is distributed on an - * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express - * or implied. See the License for the specific language governing - * rights and limitations under the License. + * Created by: Nickolay Samofatov * - * The Original Code was created by Inprise Corporation - * and its predecessors. Portions created by Inprise Corporation are - * Copyright (C) Inprise Corporation. + * Contributor(s): + * * - * Created by: Nickolay Samofatov - * - * All Rights Reserved. - * Contributor(s): ______________________________________. + * $Id: nbak.cpp,v 1.8 2003-09-08 20:23:35 skidder Exp $ * */ @@ -40,6 +52,7 @@ #include "gds_proto.h" #include "os/guid.h" #include "sch_proto.h" +#include "os/isc_i_proto.h" #ifdef HAVE_UNISTD_H #include @@ -49,9 +62,62 @@ #include #endif -void BackupManager::generate_filename() { - strncpy(diff_name, (char*)database->dbb_filename->str_data, sizeof(diff_name)); - strncat(diff_name, ".delta", sizeof(diff_name)-strlen(diff_name)-1); +#ifdef NBAK_DEBUG +IMPLEMENT_TRACE_ROUTINE(nbak_trace, "NBAK"); +#endif + +/******************************** LOCK FUNCTIONS ******************************/ + +#ifndef SUPERSERVER +void BackupManager::increment_diff_use_count() throw() { +// This functions are used in CS builds to prevent closing of +// difference file while there are still unflushed pages from it +// and status already changed. Backup status does not change +// externally in SS builds so we have no point to track this usage +// except for debugging purposes + NBAK_TRACE(("increment_diff_use_count %d", diff_use_count)); + diff_use_count++; +} + +void BackupManager::decrement_diff_use_count() throw() { + NBAK_TRACE(("decrement_diff_use_count %d", diff_use_count)); + diff_use_count--; +} +#endif + +bool BackupManager::get_sw_database_lock(bool thread_exit, bool enable_signals) throw() { +#ifdef SUPERSERVER + if (thread_exit) THREAD_EXIT; + database_lock->beginRead(); + if (thread_exit) THREAD_ENTER; + return true; +#else + NBAK_TRACE(("get_sw_database_lock %d", database_use_count)); + TDBB tdbb = GET_THREAD_DATA; + database_use_count++; + if (enable_signals) LCK_ast_enable(); + if (database_lock->lck_physical >= LCK_SW) return true; + if (!LCK_lock(tdbb, database_lock, LCK_SW, LCK_WAIT)) { + gds__log("Cannot take SW lock on database"); + return false; + } + return true; +#endif +} + +void BackupManager::release_sw_database_lock() throw() { +#ifdef SUPERSERVER + database_lock->endRead(); +#else + NBAK_TRACE(("release_sw_database_lock %d", database_use_count)); + assert(database_use_count > 0); + TDBB tdbb = GET_THREAD_DATA; + database_use_count--; + if (ast_flags & NBAK_database_blocking) { + LCK_release(tdbb, database_lock); + ast_flags &= ~NBAK_database_blocking; + } +#endif } void BackupManager::lock_state_write(bool thread_exit) { @@ -60,17 +126,30 @@ void BackupManager::lock_state_write(bool thread_exit) { state_lock->beginWrite(); if (thread_exit) THREAD_ENTER; #else - ast_flags |= NBAK_state_in_use; // Prevent ASTs from releasing the lock + assert(!(flags & NBAK_state_in_use)); TDBB tdbb = GET_THREAD_DATA; + flags |= NBAK_state_in_use; + bool locked = false; // Release shared lock to prevent possible deadlocks if (state_lock->lck_physical != LCK_none) if (LCK_convert(tdbb, state_lock, LCK_EX, LCK_NO_WAIT)) - return; - else + locked = true; + else { LCK_release(tdbb, state_lock); - if (!LCK_lock(tdbb, state_lock, LCK_EX, LCK_WAIT)) + ast_flags &= ~NBAK_state_blocking; + backup_state = nbak_state_unknown; + } + if (!locked && !LCK_lock(tdbb, state_lock, LCK_EX, LCK_WAIT)) { + flags &= ~NBAK_state_in_use; // Lock should be released at this point + gds__log("Cannot lock database backup state for writing"); // This is OK because state changing code expect it ERR_post(gds_lock_conflict, 0); + } + NBAK_TRACE(("backup state locked for writing")); + if (!actualize_state()) { + unlock_state_write(); + ERR_punt(); + } #endif } @@ -78,522 +157,104 @@ bool BackupManager::try_lock_state_write() { #ifdef SUPERSERVER return state_lock->tryBeginWrite(); #else + assert(!(flags & NBAK_state_in_use)); TDBB tdbb = GET_THREAD_DATA; - ast_flags |= NBAK_state_in_use; // Prevent ASTs from releasing the lock + flags |= NBAK_state_in_use; bool result; if (state_lock->lck_physical == LCK_none) result = LCK_lock(tdbb, state_lock, LCK_EX, LCK_NO_WAIT); else result = LCK_convert(tdbb, state_lock, LCK_EX, LCK_NO_WAIT); - if (!result) { + if (result) { + NBAK_TRACE(("backup state locked for writing")); + if (!actualize_state()) { + unlock_state_write(); + ERR_punt(); + } + } + else { + flags &= ~NBAK_state_in_use; + // This code is ok only because ASTs are delivered only once per request if (ast_flags & NBAK_state_blocking) { LCK_release(tdbb, state_lock); ast_flags &= ~NBAK_state_blocking; - backup_state = nbak_state_unknown; // We know state only as long we lock it + backup_state = nbak_state_unknown; } - ast_flags &= ~NBAK_state_in_use; } return result; #endif } -void BackupManager::unlock_state_write() { +void BackupManager::unlock_state_write() throw() { #ifdef SUPERSERVER state_lock->endWrite(); #else + assert(flags & NBAK_state_in_use); TDBB tdbb = GET_THREAD_DATA; - LCK_release(tdbb, state_lock); - backup_state = nbak_state_unknown; // We know state only as long we lock it - ast_flags &= ~NBAK_state_in_use; + // ASTs are going to be reposted after CONVERT + ast_flags &= ~NBAK_state_blocking; + LCK_convert(tdbb, state_lock, LCK_SR, LCK_WAIT); + flags &= ~NBAK_state_in_use; + if (ast_flags & NBAK_state_blocking) { + LCK_release(tdbb, state_lock); + ast_flags &= ~NBAK_state_blocking; + backup_state = nbak_state_unknown; + } #endif } -bool BackupManager::lock_alloc_write(bool thread_exit) { +bool BackupManager::lock_alloc_write(bool thread_exit) throw() { #ifdef SUPERSERVER if (thread_exit) THREAD_EXIT; alloc_lock->beginWrite(); if (thread_exit) THREAD_ENTER; return true; #else - ast_flags |= NBAK_alloc_in_use; // Prevent ASTs from releasing the lock + assert(!(flags & NBAK_alloc_in_use)); TDBB tdbb = GET_THREAD_DATA; + flags |= NBAK_alloc_in_use; // Release shared lock to prevent possible deadlocks + bool locked = false; if (alloc_lock->lck_physical != LCK_none) - if (LCK_convert(tdbb, alloc_lock, LCK_EX, LCK_NO_WAIT)) - return true; - else + if (LCK_convert(tdbb, alloc_lock, LCK_EX, LCK_NO_WAIT)) { + locked = true; + } else { LCK_release(tdbb, alloc_lock); - return LCK_lock(tdbb, alloc_lock, LCK_EX, LCK_WAIT); + ast_flags &= ~NBAK_alloc_blocking; + ast_flags |= NBAK_alloc_dirty; + } + if (!locked && !LCK_lock(tdbb, alloc_lock, LCK_EX, LCK_WAIT)) { + flags &= ~NBAK_alloc_in_use; + gds__log("Cannot lock backup allocation table for writing"); + return false; + } + if (!actualize_alloc()) { + unlock_alloc_write(); + return false; + } + return true; #endif } -void BackupManager::unlock_alloc_write() { +void BackupManager::unlock_alloc_write() throw() { #ifdef SUPERSERVER alloc_lock->endWrite(); #else + assert(flags & NBAK_alloc_in_use); TDBB tdbb = GET_THREAD_DATA; + // ASTs are going to be reposted after CONVERT + ast_flags &= ~NBAK_alloc_blocking; + LCK_convert(tdbb, alloc_lock, LCK_SR, LCK_WAIT); + flags &= ~NBAK_alloc_in_use; if (ast_flags & NBAK_alloc_blocking) { LCK_release(tdbb, alloc_lock); ast_flags &= ~NBAK_alloc_blocking; ast_flags |= NBAK_alloc_dirty; - } else - LCK_convert(tdbb, alloc_lock, LCK_SR, LCK_WAIT); - ast_flags &= ~NBAK_alloc_in_use; + } #endif } -// Initialize and open difference file for writing -void BackupManager::begin_backup() { - TRACE("begin_backup"); - TDBB tdbb = GET_THREAD_DATA; - - // Lock header page first to prevent possible deadlock - WIN window; - window.win_page = HEADER_PAGE; - window.win_flags = 0; - HDR header = (HDR) CCH_FETCH(tdbb, &window, LCK_write, pag_header); - bool state_locked = false, header_locked = true; - try { - lock_state_write(true); - state_locked = true; - TRACE("state locked"); - - if (!actualize_state(tdbb->tdbb_status_vector)) - ERR_punt(); - // Check state - if (backup_state != nbak_state_normal) { - TRACE1("end backup - invalid state %d", backup_state); - unlock_state_write(); - CCH_RELEASE(tdbb, &window); - return; - } - // Create file - TRACE1("Creating difference file %s", diff_name); - diff_file = PIO_create(database, diff_name, strlen(diff_name), TRUE); - // Zero out first page (empty allocation table) - if (!alloc_buffer) - alloc_buffer = reinterpret_cast ( - FB_ALIGN((U_IPTR)FB_NEW(*database->dbb_permanent) - char[database->dbb_page_size + MIN_PAGE_SIZE], MIN_PAGE_SIZE)); - bdb temp_bdb; - temp_bdb.bdb_page = 0; - temp_bdb.bdb_dbb = database; - temp_bdb.bdb_buffer = reinterpret_cast(alloc_buffer); - memset(alloc_buffer, 0, database->dbb_page_size); - if (!PIO_write(diff_file, &temp_bdb, (PAG)alloc_buffer, tdbb->tdbb_status_vector)) - ERR_punt(); - TRACE("Set backup state in header"); - FB_GUID guid; - GenerateGuid(&guid); - tdbb->tdbb_flags |= TDBB_set_backup_state; - // Set state in database header page. All changes are written to main database file yet. - CCH_MARK_MUST_WRITE(tdbb, &window); - int newState = nbak_state_stalled; - header->hdr_flags = (header->hdr_flags & ~hdr_backup_mask) | newState; - // This number may be smaller than actual because some pages may be not flushed to - // disk yet. This is not a problem as it can cause only a slight performance degradation - backup_pages = header->hdr_backup_pages = PIO_act_alloc(database); - ULONG adjusted_scn = ++header->hdr_header.pag_scn(); // Generate new SCN - PAG_replace_entry_first(header, HDR_backup_guid, sizeof(guid), (UCHAR*)&guid); - - header_locked = false; - CCH_RELEASE(tdbb, &window); - CCH_flush(tdbb, FLUSH_ALL, 0); - tdbb->tdbb_flags &= ~TDBB_set_backup_state; - - backup_state = newState; - current_scn = adjusted_scn; - // This is essential for SuperServer to prevent multi-threading issues - // We already modified state in header, error here is not very important - actualize_alloc(tdbb->tdbb_status_vector); - unlock_state_write(); - } catch (const std::exception&) { - backup_state = nbak_state_unknown; - tdbb->tdbb_flags &= ~TDBB_set_backup_state; - if (state_locked) - unlock_state_write(); - if (header_locked) - CCH_RELEASE(tdbb, &window); - throw; - } -} - -// Merge difference file to main files (if needed) and unlink() difference -// file then. If merge is already in progress method silently returns and -// does nothing (so it can be used for recovery on database startup). -void BackupManager::end_backup(bool recover) { - TRACE("end_backup"); - TDBB tdbb = GET_THREAD_DATA; - ULONG adjusted_scn; // We use this value to prevent race conditions. - // They are possible because we release state lock - // for some instants and anything is possible at - // that times. - - // Lock header page first to prevent possible deadlock - WIN window; - window.win_page = HEADER_PAGE; - window.win_flags = 0; - HDR header = (HDR) CCH_FETCH(tdbb, &window, LCK_write, pag_header); - bool state_locked = false, header_locked = true; - - try { - if (recover) { - if (!try_lock_state_write()) { - CCH_RELEASE(tdbb, &window); - return; - } - } else - lock_state_write(true); - state_locked = true; - TRACE("state locked"); - // Check state - if (!actualize_state(tdbb->tdbb_status_vector)) - ERR_punt(); - TRACE1("Backup state at start of end_backup is %d", backup_state); - if (backup_state == nbak_state_normal || (recover && backup_state != nbak_state_merge)) { - TRACE1("invalid state %d", backup_state); - unlock_state_write(); - CCH_RELEASE(tdbb, &window); - return; - } - TRACE1("difference file %s", diff_name); - // Set state in database header - tdbb->tdbb_flags |= TDBB_set_backup_state; - TRACE1("Current backup state is %d", backup_state); - backup_state = nbak_state_merge; - adjusted_scn = ++current_scn; - TRACE1("New state is getting to become %d", backup_state); - CCH_MARK_MUST_WRITE(tdbb, &window); - TRACE1("New state is getting to become after fetches %d", backup_state); - // Generate new SCN - header->hdr_header.pag_scn() = current_scn; - TRACE1("new SCN=%d is getting written to header", adjusted_scn); - // Adjust state - header->hdr_flags = (header->hdr_flags & ~hdr_backup_mask) | backup_state; - header_locked = false; - CCH_RELEASE(tdbb, &window); - CCH_flush(tdbb, FLUSH_ALL, 0); // This is to adjust header in main database file - tdbb->tdbb_flags &= ~TDBB_set_backup_state; - TRACE1("Set state %d in header page", backup_state); - } catch (const std::exception&) { - backup_state = nbak_state_unknown; - tdbb->tdbb_flags &= ~TDBB_set_backup_state; - if (state_locked) - unlock_state_write(); - if (header_locked) - CCH_RELEASE(tdbb, &window); - throw; - } - - - // Here comes the dirty work. We need to reapply all changes from difference file to database - - // Release write state lock and get read lock. - // Merge process should not inhibit normal operations. - tdbb->tdbb_flags &= ~TDBB_set_backup_state; - unlock_state_write(); - if (!lock_state(true)) - ERR_post(gds_lock_conflict, 0); - try { - if (!actualize_state(tdbb->tdbb_status_vector)) - ERR_punt(); - TRACE3("Merge. State=%d, current_scn=%d, adjusted_scn=%d", backup_state, current_scn, adjusted_scn); - if (backup_state != nbak_state_merge || current_scn != adjusted_scn) { - /* Handle the case when somebody finalized merge for us */ - unlock_state(); - return; - } - TRACE("Status OK."); - if (!actualize_alloc(tdbb->tdbb_status_vector)) - ERR_punt(); - TRACE1("Allocation table %p is current.", alloc_table); - AllocItemTree::Accessor all(alloc_table); - - tdbb->tdbb_flags |= TDBB_set_backup_state | TDBB_backup_merge; - - if (all.getFirst()) do { - WIN window; - window.win_page = all.current().db_page; - window.win_flags = 0; - PAG page = CCH_FETCH(tdbb, &window, LCK_write, pag_undefined); - if (page->pag_scn() != current_scn) - CCH_MARK_MUST_WRITE(tdbb, &window); - CCH_RELEASE(tdbb, &window); - } while(all.getNext()); - CCH_flush(tdbb, FLUSH_ALL, 0); // Really write changes to main database file - - tdbb->tdbb_flags &= ~(TDBB_set_backup_state | TDBB_backup_merge); - unlock_state(); - - } catch(const std::exception&) { - tdbb->tdbb_flags &= ~(TDBB_set_backup_state | TDBB_backup_merge); - unlock_state(); - throw; - } - - // We finished. We need to reflect it in our database header page - window.win_page = HEADER_PAGE; - window.win_flags = 0; - header = (HDR) CCH_FETCH(tdbb, &window, LCK_write, pag_header); - state_locked = false; - header_locked = true; - try { - lock_state_write(true); - state_locked = true; - // Check state - if (!actualize_state(tdbb->tdbb_status_vector)) - ERR_punt(); - if (backup_state != nbak_state_merge || current_scn != adjusted_scn) { - /* Handle the case when somebody finalized merge for us */ - unlock_state_write(); - CCH_RELEASE(tdbb, &window); - return; - } - // Set state in database header - tdbb->tdbb_flags |= TDBB_set_backup_state; - backup_state = nbak_state_normal; - CCH_MARK_MUST_WRITE(tdbb, &window); - // Adjust state - header->hdr_flags = (header->hdr_flags & ~hdr_backup_mask) | backup_state; - TRACE1("Set state %d in header page", backup_state); - header_locked = false; - CCH_RELEASE(tdbb, &window); - CCH_flush(tdbb, FLUSH_ALL, 0); // This is to adjust header in main database file - tdbb->tdbb_flags &= ~TDBB_set_backup_state; - - // Page allocation table cache is no longer valid - delete alloc_table; - alloc_table = NULL; - last_allocated_page = 0; - - if (diff_file) { - PIO_close(diff_file); - diff_file = NULL; - } - unlink(diff_name); - - unlock_state_write(); - TRACE("backup ended"); - } catch (const std::exception&) { - backup_state = nbak_state_unknown; - tdbb->tdbb_flags &= ~TDBB_set_backup_state; - if (state_locked) - unlock_state_write(); - if (header_locked) - CCH_RELEASE(tdbb, &window); - throw; - } - return; -} - -bool BackupManager::actualize_alloc(ISC_STATUS* status) { - if (alloc_table && - // During merge cached allocation table contains pages for - // SCN number current_scn-1, not for current SCN - ((backup_state!=nbak_state_merge && alloc_scn == current_scn) || - (backup_state==nbak_state_merge && alloc_scn+1 == current_scn)) -#ifndef SUPERSERVER - && !(ast_flags & NBAK_alloc_dirty) -#endif - ) return true; - TRACE("actualize_alloc"); - // For SuperServer this routine is really executed only at database startup when - // it has exlock or when exclusive access to database is enabled - if ((backup_state!=nbak_state_merge && alloc_scn == current_scn) || - (backup_state==nbak_state_merge && alloc_scn+1 == current_scn)) - { - delete alloc_table; - alloc_table = NULL; - last_allocated_page = 0; - alloc_scn = backup_state==nbak_state_merge ? current_scn-1 : current_scn; - } - if (!alloc_table) - alloc_table = FB_NEW(*database->dbb_permanent) AllocItemTree(database->dbb_permanent); - if (!alloc_buffer) - alloc_buffer = reinterpret_cast ( - FB_ALIGN((U_IPTR)FB_NEW(*database->dbb_permanent) char[database->dbb_page_size + MIN_PAGE_SIZE], - MIN_PAGE_SIZE)); - while (true) { - bdb temp_bdb; - // Difference file pointer pages have one ULONG as number of pages allocated on the page and - // then go physical numbers of pages from main database file. Offsets of numbers correspond - // to difference file pages. - - // Get offset of pointer page. We can do so because page sizes are powers of 2 - temp_bdb.bdb_page = last_allocated_page & ~(database->dbb_page_size/sizeof(ULONG)-1); - temp_bdb.bdb_dbb = database; - temp_bdb.bdb_buffer = reinterpret_cast(alloc_buffer); - - if (!PIO_read(diff_file, &temp_bdb, (PAG)alloc_buffer, status)) - return false; - for (ULONG i=last_allocated_page-temp_bdb.bdb_page; i < alloc_buffer[0]; i++) - alloc_table->add(AllocItem(alloc_buffer[i+1], temp_bdb.bdb_page+i+1)); - last_allocated_page = temp_bdb.bdb_page + alloc_buffer[0]; - if (alloc_buffer[0] == database->dbb_page_size/sizeof(ULONG)-1) - // if page is full adjust position for next pointer page - last_allocated_page++; - else - // We finished reading allocation table - break; - } -#ifndef SUPERSERVER - ast_flags &= ~NBAK_alloc_dirty; -#endif - return true; -} - -// Return page index in difference file that can be used in -// write_difference call later. -ULONG BackupManager::get_page_index(ULONG db_page) const { - AllocItemTree::Accessor a(alloc_table); - if (a.locate(db_page)) - return a.current().diff_page; - else - return 0; -} - -// Mark next difference page as used by some database page -bool BackupManager::mark_alloc(ISC_STATUS* status, ULONG db_page) { - // Grow file first. This is done in such order to keep difference - // file consistent in case of write error. We should always be able - // to read next alloc page when previous one is full. - if (alloc_buffer[0] == database->dbb_page_size/sizeof(ULONG)-2) { - // Pointer page is full. Its time to create new one. - char *spare_buffer = FB_NEW(*database->dbb_permanent) char[database->dbb_page_size + MIN_PAGE_SIZE]; - PAG spare_page = (PAG)FB_ALIGN((U_IPTR)spare_buffer, MIN_PAGE_SIZE); - - bdb temp_bdb; - temp_bdb.bdb_page = last_allocated_page+2; - temp_bdb.bdb_dbb = database; - temp_bdb.bdb_buffer = reinterpret_cast(alloc_buffer); - memset(spare_page, 0, database->dbb_page_size); - if (!PIO_write(diff_file, &temp_bdb, spare_page, status)) { - delete spare_buffer; - return false; - } - delete spare_buffer; - } - - // Write new item to the allocation table - bdb temp_bdb; - temp_bdb.bdb_page = last_allocated_page & ~(database->dbb_page_size/sizeof(ULONG)-1); - temp_bdb.bdb_dbb = database; - temp_bdb.bdb_buffer = reinterpret_cast(alloc_buffer); - alloc_buffer[++alloc_buffer[0]] = db_page; - if (!PIO_write(diff_file, &temp_bdb, (PAG)alloc_buffer, status)) - return false; - last_allocated_page++; - // Register new page in the alloc table - alloc_table->add(AllocItem(db_page, last_allocated_page)); - // Adjust buffer and counters if we allocated new alloc page earlier - if (alloc_buffer[0] == database->dbb_page_size/sizeof(ULONG)-1) { - last_allocated_page++; - memset(alloc_buffer, 0, database->dbb_page_size); - } - - return true; -} - -bool BackupManager::write_difference(ISC_STATUS* status, ULONG diff_page, struct pag* page) { - bdb temp_bdb; - temp_bdb.bdb_page = diff_page; - temp_bdb.bdb_dbb = database; - temp_bdb.bdb_buffer = page; - if (!PIO_write(diff_file, &temp_bdb, page, status)) - return false; - return true; -} - -bool BackupManager::read_difference(ISC_STATUS* status, ULONG diff_page, struct pag* page) { - bdb temp_bdb; - temp_bdb.bdb_page = diff_page; - temp_bdb.bdb_dbb = database; - temp_bdb.bdb_buffer = page; - if (!PIO_read(diff_file, &temp_bdb, page, status)) - return false; - return true; -} - -BackupManager::BackupManager(DBB _database, int ini_state) : - database(_database), diff_file(NULL), alloc_table(NULL), - backup_state(ini_state), last_allocated_page(0), - alloc_buffer(NULL), current_scn(0), alloc_scn(0) -{ - TDBB tdbb = GET_THREAD_DATA; - diff_name[0] = 0; - -#ifdef SUPERSERVER - alloc_lock = FB_NEW(*_database->dbb_permanent) Firebird::RWLock(); - state_lock = FB_NEW(*_database->dbb_permanent) Firebird::RWLock(); - adjust_state_lock = FB_NEW(*_database->dbb_permanent) Firebird::Spinlock(); -#else - ast_flags = 0; - - state_lock = FB_NEW_RPT(*database->dbb_permanent, 0) lck(); - state_lock->lck_type = LCK_backup_state; - state_lock->lck_owner_handle = LCK_get_owner_handle(tdbb, state_lock->lck_type); - state_lock->lck_parent = database->dbb_lock; - state_lock->lck_length = 0; - state_lock->lck_dbb = database; - state_lock->lck_object = reinterpret_cast(database); - state_lock->lck_ast = backup_state_ast; - - alloc_lock = FB_NEW_RPT(*database->dbb_permanent, 0) lck(); - alloc_lock->lck_type = LCK_backup_alloc; - alloc_lock->lck_owner_handle = LCK_get_owner_handle(tdbb, alloc_lock->lck_type); - alloc_lock->lck_parent = database->dbb_lock; - alloc_lock->lck_length = 0; - alloc_lock->lck_dbb = database; - alloc_lock->lck_object = reinterpret_cast(database); - alloc_lock->lck_ast = alloc_table_ast; -#endif -} - -void BackupManager::shutdown_locks() { -#ifndef SUPERSERVER - TDBB tdbb = GET_THREAD_DATA; - if (state_lock) - LCK_release(tdbb, state_lock); - if (alloc_lock) - LCK_release(tdbb, alloc_lock); -#endif -} - - -BackupManager::~BackupManager() { - if (diff_file) - PIO_close(diff_file); - shutdown_locks(); - // Note ! We do not free memory for any allocated objects. - // It was allocated from database pool and will be freed automatically -#ifdef SUPERSERVER - delete alloc_lock; - delete state_lock; - delete adjust_state_lock; -#endif -} - -void BackupManager::set_difference(const char* filename) { - TDBB tdbb = GET_THREAD_DATA; - - if (filename) { - WIN window; - HDR header; - window.win_page = HEADER_PAGE; - window.win_flags = 0; - header = (HDR) CCH_FETCH(tdbb, &window, LCK_write, pag_header); - CCH_MARK_MUST_WRITE(tdbb, &window); - PAG_replace_entry_first(header, HDR_difference_file, - strlen(filename), (UCHAR*)filename); - CCH_RELEASE(tdbb, &window); - strncpy(diff_name, filename, sizeof(diff_name)); - } else { - PAG_delete_clump_entry(HEADER_PAGE, HDR_difference_file); - generate_filename(); - } -} - -bool BackupManager::lock_state(bool thread_exit) { +bool BackupManager::lock_state(bool thread_exit) throw() { TDBB tdbb = GET_THREAD_DATA; // If we own exlock here no need to do anything else if (tdbb->tdbb_flags & TDBB_set_backup_state) return true; @@ -603,169 +264,85 @@ bool BackupManager::lock_state(bool thread_exit) { if (thread_exit) THREAD_ENTER; return true; #else - ast_flags |= NBAK_state_in_use; // Prevent ASTs from releasing the lock - if (state_lock->lck_physical < LCK_SR && - !LCK_lock(tdbb, state_lock, LCK_SR, LCK_WAIT)) - { - ast_flags &= ~NBAK_state_in_use; // Prevent ASTs from releasing the lock + assert(!(flags & NBAK_state_in_use)); + flags |= NBAK_state_in_use; + if (state_lock->lck_physical < LCK_SR) { + if (!LCK_lock(tdbb, state_lock, LCK_SR, LCK_WAIT)) { + flags &= ~NBAK_state_in_use; + gds__log("Cannot lock database backup state for reading"); + return false; + } + } + if (!actualize_state()) { + unlock_state(); return false; - } else - return true; + } + return true; #endif } -void BackupManager::unlock_state() { +void BackupManager::unlock_state() throw() { TDBB tdbb = GET_THREAD_DATA; // If we own exlock here no need to do anything else if (tdbb->tdbb_flags & TDBB_set_backup_state) return; #ifdef SUPERSERVER state_lock->endRead(); #else + assert(flags & NBAK_state_in_use); + flags &= ~NBAK_state_in_use; if (ast_flags & NBAK_state_blocking) { LCK_release(tdbb, state_lock); ast_flags &= ~NBAK_state_blocking; - backup_state = nbak_state_unknown; // We know state only as long we lock it - } - ast_flags &= ~NBAK_state_in_use; + backup_state = nbak_state_unknown; + } #endif } -bool BackupManager::lock_alloc(bool thread_exit) { - TDBB tdbb = GET_THREAD_DATA; +bool BackupManager::lock_alloc(bool thread_exit) throw() { #ifdef SUPERSERVER if (thread_exit) THREAD_EXIT; alloc_lock->beginRead(); if (thread_exit) THREAD_ENTER; return true; #else - ast_flags |= NBAK_alloc_in_use; // Prevent ASTs from releasing the lock - if (alloc_lock->lck_physical < LCK_SR && - !LCK_lock(tdbb, alloc_lock, LCK_SR, LCK_WAIT)) - { - ast_flags &= ~NBAK_alloc_in_use; + assert(!(flags & NBAK_alloc_in_use)); + TDBB tdbb = GET_THREAD_DATA; + flags |= NBAK_alloc_in_use; + if (alloc_lock->lck_physical < LCK_SR) { + if (!LCK_lock(tdbb, alloc_lock, LCK_SR, LCK_WAIT)) { + flags &= ~NBAK_alloc_in_use; + gds__log("Cannot lock backup allocation table for reading"); + return false; + } + } + if (!actualize_alloc()) { + unlock_alloc(); return false; - } else - return true; + } + return true; #endif } -void BackupManager::unlock_alloc() { - TDBB tdbb = GET_THREAD_DATA; +void BackupManager::unlock_alloc() throw() { #ifdef SUPERSERVER alloc_lock->endRead(); #else + assert(flags & NBAK_alloc_in_use); + TDBB tdbb = GET_THREAD_DATA; + flags &= ~NBAK_alloc_in_use; if (ast_flags & NBAK_alloc_blocking) { LCK_release(tdbb, alloc_lock); ast_flags &= ~NBAK_alloc_blocking; ast_flags |= NBAK_alloc_dirty; - } - ast_flags &= ~NBAK_alloc_in_use; + } #endif } -bool BackupManager::actualize_state(ISC_STATUS* status) { - if (backup_state == nbak_state_unknown) { - // State is unknown. We need to read it from the disk. - // We cannot use CCH for this because of likely recursion. - TRACE("actualize_state"); - - // Read original page from database file or shadows. - SSHORT retryCount = 0; - char *spare_buffer = FB_NEW(*database->dbb_permanent) char[database->dbb_page_size + MIN_PAGE_SIZE]; - HDR header = (HDR)FB_ALIGN((U_IPTR)spare_buffer, MIN_PAGE_SIZE); - struct bdb temp_bdb; - temp_bdb.bdb_page = HEADER_PAGE; - temp_bdb.bdb_dbb = database; - temp_bdb.bdb_buffer = reinterpret_cast(header); - FIL file = database->dbb_file; - while (!PIO_read(file, &temp_bdb, temp_bdb.bdb_buffer, status)) { - if (!CCH_rollover_to_shadow(database, file, FALSE)) { - TRACE("Shadow change error"); - delete spare_buffer; - return false; - } - if (file != database->dbb_file) - file = database->dbb_file; - else { - if (retryCount++ == 3) { - TRACE("IO error"); - delete spare_buffer; - return false; - } - } - } - - backup_state = header->hdr_flags & hdr_backup_mask; - TRACE1("backup state read from header is %d", backup_state); - current_scn = header->hdr_header.pag_scn(); - backup_pages = header->hdr_backup_pages; - - // Read difference file name from header clumplets - bool fname_found = false; - UCHAR *p = header->hdr_data; - while (true) { - switch(*p) { - case HDR_backup_guid: - p += p[1]+2; - continue; - case HDR_difference_file: - fname_found = true; - memcpy(diff_name, p+2, p[1]); - diff_name[p[1]] = 0; - } - break; - } - if (!fname_found) generate_filename(); - - if (backup_state == nbak_state_normal) { - if (diff_file) { -#ifdef SUPERSERVER - adjust_state_lock->enter(); -#endif - PIO_close(diff_file); - diff_file = NULL; -#ifdef SUPERSERVER - adjust_state_lock->leave(); -#endif - } - // Page allocation table cache is no longer valid. - if (alloc_table) { -#ifdef SUPERSERVER - adjust_state_lock->enter(); -#endif - delete alloc_table; - alloc_table = NULL; - last_allocated_page = 0; -#ifdef SUPERSERVER - adjust_state_lock->leave(); -#endif - } - } - if (backup_state != nbak_state_normal && !diff_file) { -#ifdef SUPERSERVER - adjust_state_lock->enter(); -#endif - try { - diff_file = PIO_open(database, diff_name, strlen(diff_name), 0, - NULL, diff_name, strlen(diff_name)); - } catch(const std::exception&) { - // This routine should not throw exceptions. This is almost correct - // way to handle errors except that we do not fill passed status vector. - // It is going to be tdbb->tdbb_status_vector anyway. - return false; - } -#ifdef SUPERSERVER - adjust_state_lock->leave(); -#endif - } - - } - return true; -} +/********************************* AST FUNCTIONS ******************************/ #ifndef SUPERSERVER -int BackupManager::backup_state_ast(void *ast_object) +int BackupManager::backup_state_ast(void *ast_object) throw() { /************************************** * @@ -787,8 +364,6 @@ int BackupManager::backup_state_ast(void *ast_object) lock = new_dbb->backup_manager->state_lock; - if (lock->lck_physical != LCK_SR) - return 0; ISC_ast_enter(); @@ -803,9 +378,9 @@ int BackupManager::backup_state_ast(void *ast_object) tdbb->tdbb_request = NULL; tdbb->tdbb_transaction = NULL; - TRACE("backup_state_ast"); + NBAK_TRACE_AST("NBAK,backup_state_ast"); - if (new_dbb->backup_manager->ast_flags & NBAK_state_in_use) + if (new_dbb->backup_manager->flags & NBAK_state_in_use) new_dbb->backup_manager->ast_flags |= NBAK_state_blocking; else { // We know state only as long we lock it @@ -821,7 +396,7 @@ int BackupManager::backup_state_ast(void *ast_object) return 0; } -int BackupManager::alloc_table_ast(void *ast_object) +int BackupManager::alloc_table_ast(void *ast_object) throw() { /************************************** * @@ -857,9 +432,9 @@ int BackupManager::alloc_table_ast(void *ast_object) tdbb->tdbb_request = NULL; tdbb->tdbb_transaction = NULL; - TRACE("alloc_table_ast"); + NBAK_TRACE_AST("NBAK,alloc_table_ast"); - if (new_dbb->backup_manager->ast_flags & NBAK_alloc_in_use) + if (new_dbb->backup_manager->flags & NBAK_alloc_in_use) new_dbb->backup_manager->ast_flags |= NBAK_alloc_blocking; else { new_dbb->backup_manager->ast_flags |= NBAK_alloc_dirty; @@ -874,4 +449,726 @@ int BackupManager::alloc_table_ast(void *ast_object) return 0; } +int BackupManager::backup_database_ast(void *ast_object) throw() +{ +/************************************** + * + * b a c k u p _ d a t a b a s e _ a s t + * + ************************************** + * + * Functional description + * A blocking AST has been issued to give up + * the lock on the backup state semaphore. + * Do so if we are not using backup state. + * Otherwise set blocking flag and blocking routine + * will release the lock as soon it finishes + * + **************************************/ + DBB new_dbb = reinterpret_cast(ast_object); + LCK lock; + struct tdbb thd_context, *tdbb; + ISC_STATUS_ARRAY ast_status; + + + lock = new_dbb->backup_manager->database_lock; + + ISC_ast_enter(); + +/* Since this routine will be called asynchronously, we must establish + a thread context. */ + + SET_THREAD_DATA; + + tdbb->tdbb_database = new_dbb; + tdbb->tdbb_attachment = lock->lck_attachment; + tdbb->tdbb_quantum = QUANTUM; + tdbb->tdbb_request = NULL; + tdbb->tdbb_transaction = NULL; + tdbb->tdbb_status_vector = ast_status; + + NBAK_TRACE_AST("NBAK,backup_database_ast"); + + if (new_dbb->backup_manager->database_use_count) { + new_dbb->backup_manager->ast_flags |= NBAK_database_blocking; + ast_status[1] = 0; + CCH_flush_database(tdbb); // This may release database lock + if (ast_status[1]) + gds__log_status(new_dbb->dbb_file->fil_string, ast_status); + } + +/* Restore the prior thread context */ + + RESTORE_THREAD_DATA; + + ISC_ast_exit(); + return 0; +} + #endif + +/********************************** CORE LOGIC ********************************/ + +void BackupManager::generate_filename() throw() { + strncpy(diff_name, (char*)database->dbb_filename->str_data, sizeof(diff_name)); + strncat(diff_name, ".delta", sizeof(diff_name)-strlen(diff_name)-1); +} + +// Initialize and open difference file for writing +void BackupManager::begin_backup() { + NBAK_TRACE(("begin_backup")); + TDBB tdbb = GET_THREAD_DATA; + + // Lock header page first to prevent possible deadlock + WIN window; + window.win_page = HEADER_PAGE; + window.win_flags = 0; + HDR header = (HDR) CCH_FETCH(tdbb, &window, LCK_write, pag_header); + bool state_locked = false, header_locked = true; + try { + lock_state_write(true); + state_locked = true; + NBAK_TRACE(("state locked")); + + // Check state + if (backup_state != nbak_state_normal) { + NBAK_TRACE(("end backup - invalid state %d", backup_state)); + unlock_state_write(); + CCH_RELEASE(tdbb, &window); + return; + } + // Create file + NBAK_TRACE(("Creating difference file %s", diff_name)); + diff_file = PIO_create(database, diff_name, strlen(diff_name), TRUE); + // Zero out first page (empty allocation table) + bdb temp_bdb; + temp_bdb.bdb_page = 0; + temp_bdb.bdb_dbb = database; + temp_bdb.bdb_buffer = reinterpret_cast(alloc_buffer); + memset(alloc_buffer, 0, database->dbb_page_size); + if (!PIO_write(diff_file, &temp_bdb, temp_bdb.bdb_buffer, tdbb->tdbb_status_vector)) + ERR_punt(); + NBAK_TRACE(("Set backup state in header")); + FB_GUID guid; + GenerateGuid(&guid); + tdbb->tdbb_flags |= TDBB_set_backup_state; + // Set state in database header page. All changes are written to main database file yet. + CCH_MARK_MUST_WRITE(tdbb, &window); + int newState = nbak_state_stalled; + header->hdr_flags = (header->hdr_flags & ~hdr_backup_mask) | newState; + // This number may be smaller than actual because some pages may be not flushed to + // disk yet. This is not a problem as it can cause only a slight performance degradation + backup_pages = header->hdr_backup_pages = PIO_act_alloc(database); + ULONG adjusted_scn = ++header->hdr_header.pag_scn(); // Generate new SCN + PAG_replace_entry_first(header, HDR_backup_guid, sizeof(guid), (UCHAR*)&guid); + + header_locked = false; + CCH_RELEASE(tdbb, &window); + tdbb->tdbb_flags &= ~TDBB_set_backup_state; + + backup_state = newState; + current_scn = adjusted_scn; + // This is essential for SuperServer to prevent multi-threading issues + // We already modified state in header, error here is not very important +#ifdef SUPERSERVER + // We cannot do this in classic because we do not have alloc_lock now + // and we'll not get alloc_table_ast at the appropriate point + actualize_alloc(); +#endif + unlock_state_write(); + } catch (const std::exception&) { + backup_state = nbak_state_unknown; + tdbb->tdbb_flags &= ~TDBB_set_backup_state; + if (state_locked) + unlock_state_write(); + if (header_locked) + CCH_RELEASE(tdbb, &window); + throw; + } + // Flush local cache + CCH_flush_database(tdbb); +#ifdef SUPERSERVER + // Check if we can take lock on main database file + if (!database_lock->tryBeginWrite()) { + ERR_bugcheck_msg("Internal error. Cannot take EX lock on database"); + } + database_lock->endWrite(); +#else + if (database_use_count) { + ERR_bugcheck_msg("Internal error. Cannot take EX lock on database when flushing local cache"); + } + // Send AST's to flush other caches + database_use_count++; + if (!LCK_lock(tdbb, database_lock, LCK_EX, LCK_WAIT)) { + ERR_bugcheck_msg( + "Internal error. Cannot take EX lock on database when flushing other processes cache"); + } + database_use_count--; + LCK_release(tdbb, database_lock); +#endif +} + +// Merge difference file to main files (if needed) and unlink() difference +// file then. If merge is already in progress method silently returns and +// does nothing (so it can be used for recovery on database startup). +void BackupManager::end_backup(bool recover) { + NBAK_TRACE(("end_backup")); + TDBB tdbb = GET_THREAD_DATA; + ULONG adjusted_scn; // We use this value to prevent race conditions. + // They are possible because we release state lock + // for some instants and anything is possible at + // that times. + + // Lock header page first to prevent possible deadlock + WIN window; + window.win_page = HEADER_PAGE; + window.win_flags = 0; + HDR header = (HDR) CCH_FETCH(tdbb, &window, LCK_write, pag_header); + bool state_locked = false, header_locked = true; + + try { + if (recover) { + if (!try_lock_state_write()) { + CCH_RELEASE(tdbb, &window); + return; + } + } else + lock_state_write(true); + state_locked = true; + NBAK_TRACE(("state locked")); + // Check state + NBAK_TRACE(("Backup state at start of end_backup is %d", backup_state)); + if (backup_state == nbak_state_normal || (recover && backup_state != nbak_state_merge)) { + NBAK_TRACE(("invalid state %d", backup_state)); + unlock_state_write(); + CCH_RELEASE(tdbb, &window); + return; + } + NBAK_TRACE(("difference file %s", diff_name)); + // Set state in database header + tdbb->tdbb_flags |= TDBB_set_backup_state; + NBAK_TRACE(("Current backup state is %d", backup_state)); + backup_state = nbak_state_merge; + adjusted_scn = ++current_scn; + NBAK_TRACE(("New state is getting to become %d", backup_state)); + CCH_MARK_MUST_WRITE(tdbb, &window); + NBAK_TRACE(("New state is getting to become after fetches %d", backup_state)); + // Generate new SCN + header->hdr_header.pag_scn() = current_scn; + NBAK_TRACE(("new SCN=%d is getting written to header", adjusted_scn)); + // Adjust state + header->hdr_flags = (header->hdr_flags & ~hdr_backup_mask) | backup_state; + header_locked = false; + CCH_RELEASE(tdbb, &window); + tdbb->tdbb_flags &= ~TDBB_set_backup_state; + NBAK_TRACE(("Set state %d in header page", backup_state)); + } catch (const std::exception&) { + backup_state = nbak_state_unknown; + tdbb->tdbb_flags &= ~TDBB_set_backup_state; + if (state_locked) + unlock_state_write(); + if (header_locked) + CCH_RELEASE(tdbb, &window); + throw; + } + + + // Here comes the dirty work. We need to reapply all changes from difference file to database + + // Release write state lock and get read lock. + // Merge process should not inhibit normal operations. + tdbb->tdbb_flags &= ~TDBB_set_backup_state; + unlock_state_write(); + if (!lock_state(true)) + ERR_punt(); + try { + NBAK_TRACE(("Merge. State=%d, current_scn=%d, adjusted_scn=%d", + backup_state, current_scn, adjusted_scn)); + if (backup_state != nbak_state_merge || current_scn != adjusted_scn) { + /* Handle the case when somebody finalized merge for us */ + unlock_state(); + return; + } + NBAK_TRACE(("Status OK.")); + if (!actualize_alloc()) + ERR_punt(); + NBAK_TRACE(("Allocation table %p is current.", alloc_table)); + AllocItemTree::Accessor all(alloc_table); + + tdbb->tdbb_flags |= TDBB_set_backup_state | TDBB_backup_merge; + + if (all.getFirst()) do { + WIN window; + window.win_page = all.current().db_page; + window.win_flags = 0; + PAG page = CCH_FETCH(tdbb, &window, LCK_write, pag_undefined); + if (page->pag_scn() != current_scn) + CCH_MARK(tdbb, &window); + CCH_RELEASE(tdbb, &window); + } while(all.getNext()); + CCH_flush(tdbb, FLUSH_ALL, 0); // Really write changes to main database file + + tdbb->tdbb_flags &= ~(TDBB_set_backup_state | TDBB_backup_merge); + unlock_state(); + + } catch(const std::exception&) { + tdbb->tdbb_flags &= ~(TDBB_set_backup_state | TDBB_backup_merge); + unlock_state(); + throw; + } + + // We finished. We need to reflect it in our database header page + window.win_page = HEADER_PAGE; + window.win_flags = 0; + header = (HDR) CCH_FETCH(tdbb, &window, LCK_write, pag_header); + state_locked = false; + header_locked = true; + try { + lock_state_write(true); + state_locked = true; + // Check state + if (backup_state != nbak_state_merge || current_scn != adjusted_scn) { + /* Handle the case when somebody finalized merge for us */ + unlock_state_write(); + CCH_RELEASE(tdbb, &window); + return; + } + // Set state in database header + tdbb->tdbb_flags |= TDBB_set_backup_state; + backup_state = nbak_state_normal; + CCH_MARK_MUST_WRITE(tdbb, &window); + // Adjust state + header->hdr_flags = (header->hdr_flags & ~hdr_backup_mask) | backup_state; + NBAK_TRACE(("Set state %d in header page", backup_state)); + // Generate new SCN + header->hdr_header.pag_scn() = ++current_scn; + NBAK_TRACE(("new SCN=%d is getting written to header")); + header_locked = false; + CCH_RELEASE(tdbb, &window); + tdbb->tdbb_flags &= ~TDBB_set_backup_state; + + // Page allocation table cache is no longer valid + delete alloc_table; + alloc_table = NULL; + last_allocated_page = 0; + + if (diff_file) { +#ifndef SUPERSERVER + diff_generation++; +#endif + PIO_close(diff_file); + diff_file = NULL; + } + unlink(diff_name); + + unlock_state_write(); + NBAK_TRACE(("backup ended")); + } catch (const std::exception&) { + backup_state = nbak_state_unknown; + tdbb->tdbb_flags &= ~TDBB_set_backup_state; + if (state_locked) + unlock_state_write(); + if (header_locked) + CCH_RELEASE(tdbb, &window); + throw; + } + return; +} + +bool BackupManager::actualize_alloc() throw() { + if (alloc_table +#ifndef SUPERSERVER + && !(ast_flags & NBAK_alloc_dirty) +#endif + ) return true; + ISC_STATUS *status = GET_THREAD_DATA->tdbb_status_vector; + try { + NBAK_TRACE(("actualize_alloc last_allocated_page=%d alloc_table=%p", + last_allocated_page, alloc_table)); + // For SuperServer this routine is really executed only at database startup when + // it has exlock or when exclusive access to database is enabled + if (!alloc_table) + alloc_table = FB_NEW(*database->dbb_permanent) AllocItemTree(database->dbb_permanent); + while (true) { + bdb temp_bdb; + // Difference file pointer pages have one ULONG as number of pages allocated on the page and + // then go physical numbers of pages from main database file. Offsets of numbers correspond + // to difference file pages. + + // Get offset of pointer page. We can do so because page sizes are powers of 2 + temp_bdb.bdb_page = last_allocated_page & ~(database->dbb_page_size/sizeof(ULONG)-1); + temp_bdb.bdb_dbb = database; + temp_bdb.bdb_buffer = reinterpret_cast(alloc_buffer); + + if (!PIO_read(diff_file, &temp_bdb, temp_bdb.bdb_buffer, status)) + return false; + for (ULONG i=last_allocated_page-temp_bdb.bdb_page; i < alloc_buffer[0]; i++) { + NBAK_TRACE(("alloc item page=%d, diff=%d", alloc_buffer[i+1], temp_bdb.bdb_page+i+1)); + if (!alloc_table->add(AllocItem(alloc_buffer[i+1], temp_bdb.bdb_page+i+1))) { + database->dbb_flags |= DBB_bugcheck; + status[0] = isc_arg_gds; + status[1] = gds_bug_check; + status[2] = gds_arg_string; + status[3] = (ISC_STATUS)ERR_cstring("Duplicated item in allocation table detected"); + status[4] = gds_arg_end; + return false; + } + } + last_allocated_page = temp_bdb.bdb_page + alloc_buffer[0]; + if (alloc_buffer[0] == database->dbb_page_size/sizeof(ULONG)-1) + // if page is full adjust position for next pointer page + last_allocated_page++; + else + // We finished reading allocation table + break; + } + } catch (const std::bad_alloc&) { + // Handle out of memory error + delete alloc_table; + alloc_table = NULL; + last_allocated_page = 0; + status[0] = isc_arg_gds; + status[1] = isc_virmemexh; + status[2] = isc_arg_end; + return false; + } +#ifndef SUPERSERVER + ast_flags &= ~NBAK_alloc_dirty; +#endif + return true; +} + +// Return page index in difference file that can be used in +// write_difference call later. +ULONG BackupManager::get_page_index(ULONG db_page) const throw() { + AllocItemTree::Accessor a(alloc_table); + if (a.locate(db_page)) + return a.current().diff_page; + else + return 0; +} + +// Mark next difference page as used by some database page +ULONG BackupManager::allocate_difference_page(ULONG db_page) throw() { + assert(last_allocated_page % (database->dbb_page_size/sizeof(ULONG)) == alloc_buffer[0]); + + ISC_STATUS* status = GET_THREAD_DATA->tdbb_status_vector; + // Grow file first. This is done in such order to keep difference + // file consistent in case of write error. We should always be able + // to read next alloc page when previous one is full. + bdb temp_bdb; + temp_bdb.bdb_page = last_allocated_page+1; + temp_bdb.bdb_dbb = database; + temp_bdb.bdb_buffer = reinterpret_cast(empty_buffer); + if (!PIO_write(diff_file, &temp_bdb, (PAG)empty_buffer, status)) + return 0; + + bool alloc_page_full = alloc_buffer[0] == database->dbb_page_size/sizeof(ULONG)-2; + if (alloc_page_full) { + // Pointer page is full. Its time to create new one. + temp_bdb.bdb_page = last_allocated_page+2; + temp_bdb.bdb_dbb = database; + temp_bdb.bdb_buffer = reinterpret_cast(empty_buffer); + if (!PIO_write(diff_file, &temp_bdb, (PAG)empty_buffer, status)) + return 0; + } + + // Write new item to the allocation table + temp_bdb.bdb_page = last_allocated_page & ~(database->dbb_page_size/sizeof(ULONG)-1); + temp_bdb.bdb_dbb = database; + temp_bdb.bdb_buffer = reinterpret_cast(alloc_buffer); + alloc_buffer[++alloc_buffer[0]] = db_page; + if (!PIO_write(diff_file, &temp_bdb, temp_bdb.bdb_buffer, status)) + return 0; + last_allocated_page++; + // Register new page in the alloc table + try { + alloc_table->add(AllocItem(db_page, last_allocated_page)); + } catch(const std::bad_alloc&) { + // Handle out of memory error + delete alloc_table; + alloc_table = NULL; + last_allocated_page = 0; + status[0] = isc_arg_gds; + status[1] = isc_virmemexh; + status[2] = isc_arg_end; + return 0; + } + // Adjust buffer and counters if we allocated new alloc page earlier + if (alloc_page_full) { + last_allocated_page++; + memset(alloc_buffer, 0, database->dbb_page_size); + return last_allocated_page-1; + } + + return last_allocated_page; +} + +bool BackupManager::write_difference(ISC_STATUS* status, ULONG diff_page, struct pag* page) throw() +{ + bdb temp_bdb; + temp_bdb.bdb_page = diff_page; + temp_bdb.bdb_dbb = database; + temp_bdb.bdb_buffer = page; + if (!PIO_write(diff_file, &temp_bdb, page, status)) + return false; + return true; +} + +bool BackupManager::read_difference(ULONG diff_page, struct pag* page) throw() +{ + bdb temp_bdb; + temp_bdb.bdb_page = diff_page; + temp_bdb.bdb_dbb = database; + temp_bdb.bdb_buffer = page; + if (!PIO_read(diff_file, &temp_bdb, page, GET_THREAD_DATA->tdbb_status_vector)) + return false; + return true; +} + +BackupManager::BackupManager(DBB _database, int ini_state) : + database(_database), diff_file(NULL), alloc_table(NULL), + backup_state(ini_state), last_allocated_page(0), + current_scn(0), backup_pages(0), diff_pending_close(false) +{ + TDBB tdbb = GET_THREAD_DATA; + diff_name[0] = 0; + + // Allocate various database page buffers needed for operation + BYTE *buffers = reinterpret_cast( + FB_ALIGN( + reinterpret_cast( + FB_NEW(*database->dbb_permanent) + char [database->dbb_page_size * 3 + MIN_PAGE_SIZE] + ), + MIN_PAGE_SIZE + ) + ); + memset(buffers, 0, database->dbb_page_size * 3); + + empty_buffer = reinterpret_cast(buffers); + spare_buffer = reinterpret_cast(buffers + database->dbb_page_size); + alloc_buffer = reinterpret_cast(buffers + database->dbb_page_size + 2); + + +#ifdef SUPERSERVER + alloc_lock = FB_NEW(*_database->dbb_permanent) Firebird::RWLock(); + state_lock = FB_NEW(*_database->dbb_permanent) Firebird::RWLock(); + database_lock = FB_NEW(*_database->dbb_permanent) Firebird::RWLock(); + adjust_state_lock = FB_NEW(*_database->dbb_permanent) Firebird::Spinlock(); +#else + flags = 0; + ast_flags = 0; + database_use_count = 0; + diff_use_count = 0; + diff_generation = 0; + + state_lock = FB_NEW_RPT(*database->dbb_permanent, 0) lck(); + state_lock->lck_type = LCK_backup_state; + state_lock->lck_owner_handle = LCK_get_owner_handle(tdbb, state_lock->lck_type); + state_lock->lck_parent = database->dbb_lock; + state_lock->lck_length = 0; + state_lock->lck_dbb = database; + state_lock->lck_object = reinterpret_cast(database); + state_lock->lck_ast = backup_state_ast; + + alloc_lock = FB_NEW_RPT(*database->dbb_permanent, 0) lck(); + alloc_lock->lck_type = LCK_backup_alloc; + alloc_lock->lck_owner_handle = LCK_get_owner_handle(tdbb, alloc_lock->lck_type); + alloc_lock->lck_parent = database->dbb_lock; + alloc_lock->lck_length = 0; + alloc_lock->lck_dbb = database; + alloc_lock->lck_object = reinterpret_cast(database); + alloc_lock->lck_ast = alloc_table_ast; + + database_lock = FB_NEW_RPT(*database->dbb_permanent, 0) lck(); + database_lock->lck_type = LCK_backup_database; + database_lock->lck_owner_handle = LCK_get_owner_handle(tdbb, database_lock->lck_type); + database_lock->lck_parent = database->dbb_lock; + database_lock->lck_length = 0; + database_lock->lck_dbb = database; + database_lock->lck_object = reinterpret_cast(database); + database_lock->lck_ast = backup_database_ast; +#endif +} + +void BackupManager::shutdown_locks() throw() { +#ifndef SUPERSERVER + TDBB tdbb = GET_THREAD_DATA; + if (state_lock) + LCK_release(tdbb, state_lock); + if (alloc_lock) + LCK_release(tdbb, alloc_lock); + if (database_lock) + LCK_release(tdbb, database_lock); +#endif +} + + +BackupManager::~BackupManager() { + if (diff_file) + PIO_close(diff_file); + shutdown_locks(); + // Note ! We do not free memory for any allocated objects. + // It was allocated from database pool and will be freed automatically +#ifdef SUPERSERVER + delete alloc_lock; + delete state_lock; + delete adjust_state_lock; + delete database_lock; +#endif +} + +void BackupManager::set_difference(const char* filename) { + TDBB tdbb = GET_THREAD_DATA; + + if (filename) { + WIN window; + HDR header; + window.win_page = HEADER_PAGE; + window.win_flags = 0; + header = (HDR) CCH_FETCH(tdbb, &window, LCK_write, pag_header); + CCH_MARK_MUST_WRITE(tdbb, &window); + PAG_replace_entry_first(header, HDR_difference_file, + strlen(filename), (UCHAR*)filename); + CCH_RELEASE(tdbb, &window); + strncpy(diff_name, filename, sizeof(diff_name)); + } else { + PAG_delete_clump_entry(HEADER_PAGE, HDR_difference_file); + generate_filename(); + } +} + +bool BackupManager::actualize_state() throw() { + if (backup_state != nbak_state_unknown) return true; + + // State is unknown. We need to read it from the disk. + // We cannot use CCH for this because of likely recursion. + NBAK_TRACE(("actualize_state")); + + ISC_STATUS *status = GET_THREAD_DATA->tdbb_status_vector; + + // Read original page from database file or shadows. + SSHORT retryCount = 0; + HDR header = reinterpret_cast(spare_buffer); + struct bdb temp_bdb; + temp_bdb.bdb_page = HEADER_PAGE; + temp_bdb.bdb_dbb = database; + temp_bdb.bdb_buffer = reinterpret_cast(header); + FIL file = database->dbb_file; + while (!PIO_read(file, &temp_bdb, temp_bdb.bdb_buffer, status)) { + if (!CCH_rollover_to_shadow(database, file, FALSE)) { + NBAK_TRACE(("Shadow change error")); + return false; + } + if (file != database->dbb_file) + file = database->dbb_file; + else { + if (retryCount++ == 3) { + NBAK_TRACE(("IO error")); + return false; + } + } + } + + int new_backup_state = header->hdr_flags & hdr_backup_mask; + NBAK_TRACE(("backup state read from header is %d", new_backup_state)); + // Check is we missed lock/unlock cycle and need to invalidate + // our allocation table and file handle + bool missed_cycle = + (header->hdr_header.pag_scn() - current_scn) > 1; + current_scn = header->hdr_header.pag_scn(); + backup_pages = header->hdr_backup_pages; + + // Read difference file name from header clumplets + bool fname_found = false; + UCHAR *p = header->hdr_data; + while (true) { + switch(*p) { + case HDR_backup_guid: + p += p[1]+2; + continue; + case HDR_difference_file: + fname_found = true; + memcpy(diff_name, p+2, p[1]); + diff_name[p[1]] = 0; + } + break; + } + if (!fname_found) generate_filename(); + + if (new_backup_state == nbak_state_normal || missed_cycle) { + if (diff_file) + diff_pending_close = true; + // Page allocation table cache is no longer valid. + if (alloc_table) { + NBAK_TRACE(("Dropping alloc table")); +#ifdef SUPERSERVER + adjust_state_lock->enter(); +#endif + delete alloc_table; + alloc_table = NULL; + last_allocated_page = 0; +#ifdef SUPERSERVER + adjust_state_lock->leave(); +#endif + } + } +#ifndef SUPERSERVER + if (diff_use_count && new_backup_state == nbak_state_normal) { + database->dbb_flags |= DBB_bugcheck; + status[0] = isc_arg_gds; + status[1] = gds_bug_check; + status[2] = gds_arg_string; + status[3] = (ISC_STATUS)ERR_cstring("Difference file is in use while backup is already finished"); + status[4] = gds_arg_end; + return false; + } +#endif + if (diff_pending_close +#ifndef SUPERSERVER + && diff_use_count == 0 +#endif + ) + { + NBAK_TRACE(("Close difference file")); +#ifdef SUPERSERVER + adjust_state_lock->enter(); +#endif +#ifndef SUPERSERVER + diff_generation++; +#endif + PIO_close(diff_file); + diff_file = NULL; +#ifdef SUPERSERVER + adjust_state_lock->leave(); +#endif + diff_pending_close = false; + } + if (new_backup_state != nbak_state_normal && !diff_file) { +#ifdef SUPERSERVER + adjust_state_lock->enter(); +#endif + try { + NBAK_TRACE(("Open difference file")); + diff_file = PIO_open(database, diff_name, strlen(diff_name), 0, + NULL, diff_name, strlen(diff_name)); + } catch(const Firebird::status_exception& ex) { +#ifdef SUPERSERVER + adjust_state_lock->leave(); +#endif + // This routine should not throw exceptions. This is almost correct + // way to handle errors except that we do not fill status vector. + // Error is going to be in tdbb->tdbb_status_vector anyway. + // PIO_open should not throw any other exceptions. + gds__log_status(diff_name, status); + return false; + } +#ifdef SUPERSERVER + adjust_state_lock->leave(); +#endif + } + // Adjust state at the very and to ensure proper error handling + backup_state = new_backup_state; + return true; +} diff --git a/src/jrd/nbak.h b/src/jrd/nbak.h index d4964dd74f..2e59b5bc96 100644 --- a/src/jrd/nbak.h +++ b/src/jrd/nbak.h @@ -3,26 +3,39 @@ * MODULE: nbak.h * DESCRIPTION: New backup interface definitions * - * The contents of this file are subject to the Interbase Public - * License Version 1.0 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy - * of the License at http://www.Inprise.com/IPL.html + * This library 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; either + * version 2.1 of the License, or (at your option) any later version. + * You may obtain a copy of the Licence at + * http://www.gnu.org/licences/lgpl.html + * + * As a special exception this file can also be included in modules + * with other source code as long as that source code has been + * released under an Open Source Initiative certificed licence. + * More information about OSI certification can be found at: + * http://www.opensource.org + * + * This module 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 Licence for more details. + * + * This module was created by members of the firebird development + * team. All individual contributions remain the Copyright (C) of + * those individuals and all rights are reserved. Contributors to + * this file are either listed below or can be obtained from a CVS + * history command. * - * Software distributed under the License is distributed on an - * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express - * or implied. See the License for the specific language governing - * rights and limitations under the License. + * Created by: Nickolay Samofatov * - * The Original Code was created by Inprise Corporation - * and its predecessors. Portions created by Inprise Corporation are - * Copyright (C) Inprise Corporation. + * Contributor(s): + * * - * Created by: Nickolay Samofatov + * $Id: nbak.h,v 1.4 2003-09-08 20:23:35 skidder Exp $ * - * All Rights Reserved. - * Contributor(s): ______________________________________. */ - + #ifndef _JRD_NBAK_H_ #define _JRD_NBAK_H_ @@ -30,6 +43,18 @@ #include "../common/classes/rwlock.h" #include "../common/classes/alloc.h" +// Uncomment this line if you need to trace backup-related activity +//#define NBAK_DEBUG + +#ifdef NBAK_DEBUG +DEFINE_TRACE_ROUTINE(nbak_trace); +#define NBAK_TRACE(args) nbak_trace args +#define NBAK_TRACE_AST(message) gds__trace(message) +#else +#define NBAK_TRACE(args) /* nothing */ +#define NBAK_TRACE_AST(message) /* nothing */ +#endif + class AllocItem { public: ULONG db_page; // Page number in the main database file @@ -54,7 +79,7 @@ public: // Subsystem initialization BackupManager(class dbb* _database, int ini_state); // Release locks in response to shutdown AST - void shutdown_locks(); + void shutdown_locks() throw(); // Subsystem finalization ~BackupManager(); // Set difference file name in header. @@ -62,20 +87,20 @@ public: void set_difference(const char* filename); // Prevent backup state from modification by others // You may or may not call unlock_state in case this function fails - bool lock_state(bool thread_exit); + bool lock_state(bool thread_exit) throw(); // Remove our interest in consistent backup state - void unlock_state(); + void unlock_state() throw(); // Return current backup state - int get_state() const { + int get_state() const throw() { return backup_state; } // Return current SCN for database - ULONG get_current_scn() const { + ULONG get_current_scn() const throw() { return current_scn; } // Return the amount of pages in locked database files - SLONG get_backup_pages() const { + SLONG get_backup_pages() const throw() { return backup_pages; } @@ -89,72 +114,99 @@ public: // Prevent allocation table from modification by other threads/processes // You may or may not call unlock function in case this functions fail - bool lock_alloc(bool thread_exit); - bool lock_alloc_write(bool thread_exit); + bool lock_alloc(bool thread_exit) throw(); + bool lock_alloc_write(bool thread_exit) throw(); // Remove our interest in static allocation table - void unlock_alloc(); - void unlock_alloc_write(); + void unlock_alloc() throw(); + void unlock_alloc_write() throw(); // Return page index in difference file that can be used in // write_difference call later. - ULONG get_page_index(ULONG db_page) const; + ULONG get_page_index(ULONG db_page) const throw(); // Return next page index in the difference file to be allocated - ULONG get_next_page() const { - return last_allocated_page+1; - } - // Mark next difference page as used by some database page - bool mark_alloc(ISC_STATUS* status, ULONG db_page); + ULONG allocate_difference_page(ULONG db_page) throw(); - bool write_difference(ISC_STATUS* status, ULONG diff_page, struct pag* page); + // Must have ISC_STATUS because it is called from write_page + bool write_difference(ISC_STATUS* status, ULONG diff_page, struct pag* page) throw(); - bool read_difference(ISC_STATUS* status, ULONG diff_page, struct pag* page); + bool read_difference(ULONG diff_page, struct pag* page) throw(); - bool actualize_state(ISC_STATUS* status); + + // Routines to declare and release interest in the main database file + bool get_sw_database_lock(bool thread_exit, bool enable_signals) throw(); + void release_sw_database_lock() throw(); - // Ensure that cached allocation table contains the same information as database - // Return false in case of error - bool actualize_alloc(ISC_STATUS* status); +#ifndef SUPERSERVER + // Routines to declare and release deferred interest in the difference file + void increment_diff_use_count() throw(); + void decrement_diff_use_count() throw(); + + SLONG get_current_generation() const throw() { + return diff_generation; + } +#endif private: class dbb* database; class fil* diff_file; AllocItemTree* alloc_table; // Cached allocation table of pages in difference file - int backup_state; + volatile SATOM backup_state; ULONG last_allocated_page; // Last physical page allocated in the difference file - ULONG *alloc_buffer; + ULONG *alloc_buffer, *empty_buffer, *spare_buffer; ULONG current_scn; - ULONG alloc_scn; // SCN of cached allocation table SLONG backup_pages; // Number of allocated pages as it was at point of backup lock char diff_name[MAXPATHLEN]; + // Set if we need to close difference file in the next status update cycle + // Used in CS builds to prevent closing difference file too early while it still + // may be used inside the signal handlers + bool diff_pending_close; #ifdef SUPERSERVER Firebird::RWLock* alloc_lock; // Lock to protect allocation table Firebird::RWLock* state_lock; // Lock to protect backup lock + Firebird::RWLock* database_lock; // Lock to protect write to database // Lock to protect initialization of alloc_table and diff_file // outside of exclusive status lock to prevent possible race condition // in case of errors. Firebird::Spinlock* adjust_state_lock; #else class lck* alloc_lock; // Lock to protect allocation table - class lck* state_lock; // Lock to protect backup lock - ULONG ast_flags; - static int backup_state_ast(void *ast_object); - static int alloc_table_ast(void *ast_object); + class lck* state_lock; // Lock to protect backup state + class lck* database_lock; // Lock to protect writing to database + + // Absense of 'volatile' REALLY causes database corruption + // with optimized build on Linux CS. Do not remove it ! + volatile UATOM flags; + // Variables used at AST level + volatile UATOM ast_flags; + volatile SATOM database_use_count; + volatile SATOM diff_use_count; + volatile SATOM diff_generation; + + static int backup_state_ast(void *ast_object) throw(); + static int alloc_table_ast(void *ast_object) throw(); + static int backup_database_ast(void *ast_object) throw(); #endif bool try_lock_state_write(); void lock_state_write(bool thread_exit); - void unlock_state_write(); - void generate_filename(); + void unlock_state_write() throw(); + void generate_filename() throw(); + // Make appropriate information up-to-date + bool actualize_state() throw(); + bool actualize_alloc() throw(); }; -/* Bit values for backup subsystem flags */ -#define NBAK_state_blocking 1 -#define NBAK_state_in_use 2 -#define NBAK_alloc_blocking 4 -#define NBAK_alloc_in_use 8 -#define NBAK_alloc_dirty 16 +// Flags manipulated normally +#define NBAK_state_in_use 1 +#define NBAK_alloc_in_use 2 -/* Node this flags MUST correspond with backup mask in ods.h */ -#define nbak_state_normal 0x0 /* Normal mode. Changes are simply written to main files */ -#define nbak_state_stalled 0x400 /* Main files are locked. Changes are written to diff file */ -#define nbak_state_merge 0x800 /* Merging changes from diff file into main files */ -#define nbak_state_unknown -1 /* State is unknown. Needs to be read from disk */ +// Flags manipulated at AST level +#define NBAK_state_blocking 1 +#define NBAK_alloc_blocking 2 +#define NBAK_database_blocking 4 +#define NBAK_alloc_dirty 8 + +// Note this flags MUST correspond with backup mask in ods.h +#define nbak_state_normal 0x0 // Normal mode. Changes are simply written to main files +#define nbak_state_stalled 0x400 // Main files are locked. Changes are written to diff file +#define nbak_state_merge 0x800 // Merging changes from diff file into main files +#define nbak_state_unknown -1 // State is unknown. Needs to be read from disk #endif /* _JRD_NBAK_PROTO_H_ */ diff --git a/src/jrd/os/guid.h b/src/jrd/os/guid.h index e4a65ce623..13b9bffb29 100644 --- a/src/jrd/os/guid.h +++ b/src/jrd/os/guid.h @@ -3,24 +3,37 @@ * MODULE: guid.h * DESCRIPTION: Portable GUID definition * - * The contents of this file are subject to the Interbase Public - * License Version 1.0 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy - * of the License at http://www.Inprise.com/IPL.html + * This library 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; either + * version 2.1 of the License, or (at your option) any later version. + * You may obtain a copy of the Licence at + * http://www.gnu.org/licences/lgpl.html + * + * As a special exception this file can also be included in modules + * with other source code as long as that source code has been + * released under an Open Source Initiative certificed licence. + * More information about OSI certification can be found at: + * http://www.opensource.org + * + * This module 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 Licence for more details. + * + * This module was created by members of the firebird development + * team. All individual contributions remain the Copyright (C) of + * those individuals and all rights are reserved. Contributors to + * this file are either listed below or can be obtained from a CVS + * history command. * - * Software distributed under the License is distributed on an - * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express - * or implied. See the License for the specific language governing - * rights and limitations under the License. + * Created by: Nickolay Samofatov * - * The Original Code was created by Inprise Corporation - * and its predecessors. Portions created by Inprise Corporation are - * Copyright (C) Inprise Corporation. + * Contributor(s): + * * - * Created by: Nickolay Samofatov + * $Id: guid.h,v 1.3 2003-09-08 20:23:36 skidder Exp $ * - * All Rights Reserved. - * Contributor(s): ______________________________________. */ #ifndef GUID_H diff --git a/src/jrd/os/posix/guid.cpp b/src/jrd/os/posix/guid.cpp index bbbec48b5a..0b8073ac8a 100644 --- a/src/jrd/os/posix/guid.cpp +++ b/src/jrd/os/posix/guid.cpp @@ -34,7 +34,8 @@ #include void GenerateGuid(FB_GUID *guid) { - int fd = open("/dev/random", O_RDONLY); + // do not use /dev/random because it may return lesser data than we need. + int fd = open("/dev/urandom", O_RDONLY); if (fd < 0) Firebird::system_call_failed::raise(); if (read(fd, guid, sizeof(FB_GUID)) != sizeof(FB_GUID)) diff --git a/src/jrd/os/posix/isc_ipc.cpp b/src/jrd/os/posix/isc_ipc.cpp new file mode 100644 index 0000000000..83643e502f --- /dev/null +++ b/src/jrd/os/posix/isc_ipc.cpp @@ -0,0 +1,583 @@ +/* + * PROGRAM: JRD Access Method + * MODULE: isc_ipc.c + * DESCRIPTION: Handing and posting of signals (POSIX) + * + * The contents of this file are subject to the Interbase Public + * License Version 1.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy + * of the License at http://www.Inprise.com/IPL.html + * + * Software distributed under the License is distributed on an + * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express + * or implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code was created by Inprise Corporation + * and its predecessors. Portions created by Inprise Corporation are + * Copyright (C) Inprise Corporation. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + * Solaris x86 changes - Konstantin Kuznetsov, Neil McCalden + * + * 2002.02.15 Sean Leyne - Code Cleanup, removed obsolete ports: + * - EPSON, DELTA, IMP, NCR3000 and M88K + * + * 2002.10.27 Sean Leyne - Code Cleanup, removed obsolete "UNIXWARE" port + * + * 2002.10.28 Sean Leyne - Completed removal of obsolete "DGUX" port + * + * 2002.10.29 Sean Leyne - Removed obsolete "Netware" port + * + * 2002.10.30 Sean Leyne - Removed support for obsolete "PC_PLATFORM" define + * + * 2003.08.27 Nickolay Samofatov - create POSIX version of this module + * + */ + + /* $Id: isc_ipc.cpp,v 1.1 2003-09-08 20:23:37 skidder Exp $ */ + +#include "firebird.h" +#include "../jrd/ib_stdio.h" +#include +#include +#include "../jrd/common.h" +#include "gen/codes.h" +#include "../jrd/isc.h" +#include "../jrd/gds_proto.h" +#include "../jrd/isc_proto.h" +#include "../jrd/os/isc_i_proto.h" +#include "../jrd/isc_s_proto.h" +#include "../jrd/thd_proto.h" + +#ifdef HAVE_VFORK_H +#include +#endif + +#ifdef HAVE_STRING_H +#include +#endif + +#include +#include +#include + +#ifdef HAVE_SIGNAL_H +#include +#endif + +#ifdef HAVE_SYS_SIGNAL_H +#include +#endif + +#include +#ifdef HAVE_UNISTD_H +#include +#endif + +#ifndef O_RDWR +#include +#endif + +#define LOCAL_SEMAPHORES 4 + +typedef struct sig { + struct sig *sig_next; + int sig_signal; + union { + FPTR_VOID_PTR user; + void (*client) (int, siginfo_t*, void*); + FPTR_INT_VOID_PTR informs; + FPTR_VOID untyped; + } sig_routine; + void *sig_arg; + USHORT sig_flags; +} *SIG; + +#define SIG_user 0 /* Our routine */ +#define SIG_client 1 /* Not our routine */ +#define SIG_informs 2 /* routine tells us whether to chain */ + +#define SIG_informs_continue 0 /* continue on signal processing */ +#define SIG_informs_stop 1 /* stop signal processing */ + + +static USHORT initialized_signals = FALSE; +static SIG volatile signals = NULL; +static SLONG volatile overflow_count = 0; + +#ifdef MULTI_THREAD +static MUTX_T sig_mutex; +#endif + +static int process_id = 0; + + +#ifndef GDS_RELAY +#define GDS_RELAY "/bin/gds_relay" +#endif + +static int volatile relay_pipe = 0; + + +static void cleanup(void *arg); +static void isc_signal2(int signal, FPTR_VOID handler, void *arg, ULONG); +static SLONG overflow_handler(void *arg); +static SIG que_signal(int signal, FPTR_VOID handler, void *arg, int flags); + +static void CLIB_ROUTINE signal_handler(int number, + siginfo_t *info, + void *pointer); + +#ifndef SIG_HOLD +#define SIG_HOLD SIG_DFL +#endif + +// Not thread-safe + +extern "C" { + ULONG isc_enter_count = 0; +} + +void DLL_EXPORT ISC_enter(void) +{ +/************************************** + * + * I S C _ e n t e r + * + ************************************** + * + * Functional description + * Enter ISC world from caller. + * + **************************************/ +/* Cancel our handler for SIGFPE - in case it was already there */ + ISC_signal_cancel(SIGFPE, reinterpret_cast(overflow_handler), NULL); + +/* Setup overflow handler - with chaining to any user handler */ + isc_signal2(SIGFPE, reinterpret_cast(overflow_handler), NULL, SIG_informs); + +#ifdef DEBUG_FPE_HANDLING +/* Debug code to simulate an FPE occuring during DB Operation */ + if (overflow_count < 100) + (void) kill(getpid(), SIGFPE); +#endif +} + +volatile sig_atomic_t inhibit_counter = 0; +sigset_t saved_sigmask; + +void DLL_EXPORT ISC_inhibit() throw() +{ +/************************************** + * + * I S C _ i n h i b i t + * + ************************************** + * + * Functional description + * Inhibit process of signals. Signals will be + * retained until signals are eventually re-enabled, + * then re-posted. + * + **************************************/ + if (inhibit_counter == 0) { + sigset_t set, oset; + sigfillset(&set); + sigprocmask(SIG_BLOCK, &set, &saved_sigmask); + } + inhibit_counter++; +} + + +void DLL_EXPORT ISC_enable() throw() +{ +/************************************** + * + * I S C _ e n a b l e + * + ************************************** + * + * Functional description + * Enable signal processing. Re-post any pending signals. + * + **************************************/ + assert(inhibit_counter > 0); + inhibit_counter--; + if (inhibit_counter == 0) { + // Return to the mask as it were before the first recursive + // call to ISC_inhibit + sigprocmask(SIG_SETMASK, &saved_sigmask, NULL); + } +} + + +void DLL_EXPORT ISC_exit(void) +{ +/************************************** + * + * I S C _ e x i t + * + ************************************** + * + * Functional description + * Exit ISC world, return to caller. + * + **************************************/ + +/* No longer attempt to handle overflow internally */ + ISC_signal_cancel(SIGFPE, reinterpret_cast(overflow_handler), 0); +} + + +#ifdef UNIX +int ISC_kill(SLONG pid, SLONG signal_number) +{ +/************************************** + * + * I S C _ k i l l ( U N I X ) + * + ************************************** + * + * Functional description + * Notify somebody else. + * + **************************************/ + SLONG msg[3]; + int status, pipes[2]; + TEXT process[MAXPATHLEN], arg[10]; + + for (;;) { + status = kill(pid, signal_number); + + if (!status) + return status; + if (SYSCALL_INTERRUPTED(errno)) + continue; + if (errno == EPERM) + break; + + return status; + } + +/* Process is there, but we don't have the privilege to + send to him. */ + + if (!relay_pipe) { + gds__prefix(process, GDS_RELAY); + if (pipe(pipes)) { + gds__log("ISC_kill: error %d creating gds_relay", errno); + return -1; + } + sprintf(arg, "%d", pipes[0]); + if (!vfork()) { + execl(process, process, arg, 0); + gds__log("ISC_kill: error %d starting gds_relay %s", errno, + process); + _exit(0); + } + relay_pipe = pipes[1]; + + /* Don't need the READ pipe */ + close(pipes[0]); + } + + msg[0] = pid; + msg[1] = signal_number; + msg[2] = msg[0] ^ msg[1]; /* XOR for a consistancy check */ + if (write(relay_pipe, msg, sizeof(msg)) != sizeof(msg)) { + gds__log("ISC_kill: write to relay_pipe failed %d", errno); + relay_pipe = 0; /* try to restart next time */ + return -1; + } + + return 0; +} +#endif + + +void API_ROUTINE ISC_signal(int signal_number, FPTR_VOID_PTR handler, void *arg) +{ +/************************************** + * + * I S C _ s i g n a l + * + ************************************** + * + * Functional description + * Multiplex multiple handers into single signal. + * + **************************************/ + isc_signal2(signal_number, reinterpret_cast(handler), arg, SIG_user); +} + + +static void isc_signal2( + int signal_number, + FPTR_VOID handler, void *arg, ULONG flags) +{ +/************************************** + * + * i s c _ s i g n a l 2 ( u n i x , W I N _ N T , O S 2 ) + * + ************************************** + * + * Functional description + * Multiplex multiple handers into single signal. + * + **************************************/ + + SIG sig; + +/* The signal handler needs the process id */ + if (!process_id) + process_id = getpid(); + + THD_MUTEX_LOCK(&sig_mutex); + +/* See if this signal has ever been cared about before */ + + for (sig = signals; sig; sig = sig->sig_next) + if (sig->sig_signal == signal_number) + break; + +/* If it hasn't been attach our chain handler to the signal, + and queue up whatever used to handle it as a non-ISC + routine (they are invoked differently). Note that if + the old action was SIG_DFL, SIG_HOLD, SIG_IGN or our + multiplexor, there is no need to save it. */ + + if (!sig) { + struct sigaction act, oact; + + act.sa_sigaction = signal_handler; + act.sa_flags = SA_RESTART | SA_SIGINFO; + sigemptyset(&act.sa_mask); + sigaddset(&act.sa_mask, signal_number); + sigaction(signal_number, &act, &oact); + + if ((oact.sa_flags & SA_SIGINFO && + oact.sa_sigaction != signal_handler) || + (!(oact.sa_flags & SA_SIGINFO) && + oact.sa_handler != SIG_DFL && + oact.sa_handler != SIG_HOLD && + oact.sa_handler != SIG_IGN ) + ) + { + que_signal(signal_number, + (oact.sa_flags & SA_SIGINFO) ? + reinterpret_cast(oact.sa_sigaction) : + reinterpret_cast(oact.sa_handler), + NULL, SIG_client); + } + } + + /* Que up the new ISC signal handler routine */ + + que_signal(signal_number, handler, arg, flags); + + THD_MUTEX_UNLOCK(&sig_mutex); +} + + +void API_ROUTINE ISC_signal_cancel( + int signal_number, + FPTR_VOID_PTR handler, void *arg) +{ +/************************************** + * + * I S C _ s i g n a l _ c a n c e l + * + ************************************** + * + * Functional description + * Cancel a signal handler. + * If handler == NULL, cancel all handlers for a given signal. + * + **************************************/ + SIG sig; + volatile SIG *ptr; + + THD_MUTEX_LOCK(&sig_mutex); + + for (ptr = &signals; sig = *ptr;) { + if (sig->sig_signal == signal_number && + (handler == NULL || + (sig->sig_routine.user == handler && sig->sig_arg == arg))) + { + *ptr = sig->sig_next; + gds__free(sig); + } + else + ptr = &(*ptr)->sig_next; + } + + THD_MUTEX_UNLOCK(&sig_mutex); + +} + + +void DLL_EXPORT ISC_signal_init(void) +{ +/************************************** + * + * I S C _ s i g n a l _ i n i t + * + ************************************** + * + * Functional description + * Initialize any system signal handlers. + * + **************************************/ + + if (initialized_signals) + return; + + initialized_signals = TRUE; + + overflow_count = 0; + gds__register_cleanup(cleanup, 0); + + process_id = getpid(); + + THD_MUTEX_INIT(&sig_mutex); + + isc_signal2(SIGFPE, reinterpret_cast(overflow_handler), 0, SIG_informs); + +} + + +static void cleanup(void *arg) +{ +/************************************** + * + * c l e a n u p + * + ************************************** + * + * Functional description + * Module level cleanup handler. + * + **************************************/ + signals = NULL; + + THD_MUTEX_DESTROY(&sig_mutex); + + process_id = 0; + + initialized_signals = FALSE; +} + +static SLONG overflow_handler(void *arg) +{ +/************************************** + * + * o v e r f l o w _ h a n d l e r + * + ************************************** + * + * Functional description + * Somebody overflowed. Ho hum. + * + **************************************/ + +#ifdef DEBUG_FPE_HANDLING + ib_fprintf(ib_stderr, "overflow_handler (%x)\n", arg); +#endif + +/* If we're within ISC world (inside why-value) when the FPE occurs + * we handle it (basically by ignoring it). If it occurs outside of + * ISC world, return back a code that tells signal_handler to call any + * customer provided handler. + */ + if (isc_enter_count) { + ++overflow_count; +#ifdef DEBUG_FPE_HANDLING + ib_fprintf(ib_stderr, "SIGFPE in isc code ignored %d\n", + overflow_count); +#endif + /* We've "handled" the FPE - let signal_handler know not to chain + the signal to other handlers */ + return SIG_informs_stop; + } + else { + /* We've NOT "handled" the FPE - let signal_handler know to chain + the signal to other handlers */ + return SIG_informs_continue; + } +} + +static SIG que_signal( + int signal_number, + FPTR_VOID handler, void *arg, int flags) +{ +/************************************** + * + * q u e _ s i g n a l + * + ************************************** + * + * Functional description + * Que signal for later action. + * + **************************************/ + SIG sig; + IPTR thread_id = 0; + + sig = (SIG) gds__alloc((SLONG) sizeof(struct sig)); +/* FREE: unknown */ + if (!sig) { /* NOMEM: */ + DEV_REPORT("que_signal: out of memory"); + return NULL; /* NOMEM: not handled, too difficult */ + } + +#ifdef DEBUG_GDS_ALLOC +/* This will only be freed when a signal handler is de-registered + * and we don't do that at process exit - so this not always + * a freed structure. + */ + gds_alloc_flag_unfreed((void *) sig); +#endif + + sig->sig_signal = signal_number; + sig->sig_routine.untyped = handler; + sig->sig_arg = arg; + sig->sig_flags = flags; + + sig->sig_next = signals; + signals = sig; + + return sig; +} + + +static void CLIB_ROUTINE signal_handler(int number, + siginfo_t *info, + void *pointer) +{ +/************************************** + * + * s i g n a l _ h a n d l e r ( G E N E R I C ) + * + ************************************** + * + * Functional description + * Checkin with various signal handlers. + * + **************************************/ + /* Invoke everybody who may have expressed an interest. */ + + for (SIG sig = signals; sig; sig = sig->sig_next) + if (sig->sig_signal == number) + if (sig->sig_flags & SIG_client) + (*sig->sig_routine.client)(number, info, pointer); + else if (sig->sig_flags & SIG_informs) { + /* Routine will tell us whether to chain the signal to other handlers */ + if ((*sig->sig_routine.informs)(sig->sig_arg) == SIG_informs_stop) + break; + } + else + (*sig->sig_routine.user) (sig->sig_arg); +} + diff --git a/src/jrd/os/posix/unix.cpp b/src/jrd/os/posix/unix.cpp index 5ecb5b65cc..4fce0d6b42 100644 --- a/src/jrd/os/posix/unix.cpp +++ b/src/jrd/os/posix/unix.cpp @@ -65,7 +65,7 @@ #include "../jrd/gds_proto.h" #include "../jrd/isc_proto.h" #include "../jrd/isc_f_proto.h" -#include "../jrd/isc_i_proto.h" +#include "../jrd/os/isc_i_proto.h" #include "../jrd/lck_proto.h" #include "../jrd/mov_proto.h" diff --git a/src/jrd/os/win32/guid.cpp b/src/jrd/os/win32/guid.cpp index 72e380d2be..6846174dbc 100644 --- a/src/jrd/os/win32/guid.cpp +++ b/src/jrd/os/win32/guid.cpp @@ -3,24 +3,37 @@ * MODULE: guid.h * DESCRIPTION: Portable GUID (win32) * - * The contents of this file are subject to the Interbase Public - * License Version 1.0 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy - * of the License at http://www.Inprise.com/IPL.html + * This library 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; either + * version 2.1 of the License, or (at your option) any later version. + * You may obtain a copy of the Licence at + * http://www.gnu.org/licences/lgpl.html + * + * As a special exception this file can also be included in modules + * with other source code as long as that source code has been + * released under an Open Source Initiative certificed licence. + * More information about OSI certification can be found at: + * http://www.opensource.org + * + * This module 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 Licence for more details. + * + * This module was created by members of the firebird development + * team. All individual contributions remain the Copyright (C) of + * those individuals and all rights are reserved. Contributors to + * this file are either listed below or can be obtained from a CVS + * history command. * - * Software distributed under the License is distributed on an - * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express - * or implied. See the License for the specific language governing - * rights and limitations under the License. + * Created by: Nickolay Samofatov * - * The Original Code was created by Inprise Corporation - * and its predecessors. Portions created by Inprise Corporation are - * Copyright (C) Inprise Corporation. + * Contributor(s): + * * - * Created by: Nickolay Samofatov + * $Id: guid.cpp,v 1.3 2003-09-08 20:23:38 skidder Exp $ * - * All Rights Reserved. - * Contributor(s): ______________________________________. */ #include diff --git a/src/jrd/os/win32/isc_ipc.cpp b/src/jrd/os/win32/isc_ipc.cpp new file mode 100644 index 0000000000..25450fdbfe --- /dev/null +++ b/src/jrd/os/win32/isc_ipc.cpp @@ -0,0 +1,302 @@ +/* + * PROGRAM: JRD Access Method + * MODULE: isc_ipc.c + * DESCRIPTION: Handing and posting of signals (Windows) + * + * The contents of this file are subject to the Interbase Public + * License Version 1.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy + * of the License at http://www.Inprise.com/IPL.html + * + * Software distributed under the License is distributed on an + * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express + * or implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code was created by Inprise Corporation + * and its predecessors. Portions created by Inprise Corporation are + * Copyright (C) Inprise Corporation. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + * Solaris x86 changes - Konstantin Kuznetsov, Neil McCalden + * + * 2002.02.15 Sean Leyne - Code Cleanup, removed obsolete ports: + * - EPSON, DELTA, IMP, NCR3000 and M88K + * + * 2002.10.27 Sean Leyne - Code Cleanup, removed obsolete "UNIXWARE" port + * + * 2002.10.28 Sean Leyne - Completed removal of obsolete "DGUX" port + * + * 2002.10.29 Sean Leyne - Removed obsolete "Netware" port + * + * 2002.10.30 Sean Leyne - Removed support for obsolete "PC_PLATFORM" define + * + * 2002.08.27 Nickolay Samofatov - create Windows version of this module + * + */ + + /* $Id: isc_ipc.cpp,v 1.1 2003-09-08 20:23:38 skidder Exp $ */ + +#include +#include +#include "firebird.h" +#include "../jrd/ib_stdio.h" +#include +#include "../jrd/common.h" +#include "gen/codes.h" +#include "../jrd/isc.h" +#include "../jrd/gds_proto.h" +#include "../jrd/isc_proto.h" +#include "../jrd/os/isc_i_proto.h" +#include "../jrd/isc_s_proto.h" +#include "../jrd/thd_proto.h" + +#ifdef HAVE_STRING_H +#include +#endif + +#ifndef REQUESTER +static USHORT initialized_signals = FALSE; +static SLONG volatile overflow_count = 0; + +static MUTX_T sig_mutex; + +static int process_id = 0; + +#endif /* of ifndef REQUESTER */ + + +#define MAX_OPN_EVENTS 40 + +typedef struct opn_event { + SLONG opn_event_pid; + SLONG opn_event_signal; /* pseudo-signal number */ + HANDLE opn_event_lhandle; /* local handle to foreign event */ + ULONG opn_event_age; +} *OPN_EVENT; + +static struct opn_event opn_events[MAX_OPN_EVENTS]; +static USHORT opn_event_count; +static ULONG opn_event_clock; + +static void (*system_overflow_handler)(int, int); +static SLONG overflow_handler(int, int) throw(); + +// Not thread-safe + +extern "C" { + ULONG isc_enter_count = 0; +} + +void DLL_EXPORT ISC_enter(void) +{ +/************************************** + * + * I S C _ e n t e r + * + ************************************** + * + * Functional description + * Enter ISC world from caller. + * + **************************************/ +/* Setup overflow handler - with chaining to any user handler */ + void (*temp)(int, int) = signal(SIGFPE, overflow_handler); + if (temp != overflow_handler) + system_overflow_handler = temp; + +#ifdef DEBUG_FPE_HANDLING +/* Debug code to simulate an FPE occuring during DB Operation */ + if (overflow_count < 100) + (void) kill(getpid(), SIGFPE); +#endif +} + + +void DLL_EXPORT ISC_exit(void) +{ +/************************************** + * + * I S C _ e x i t + * + ************************************** + * + * Functional description + * Exit ISC world, return to caller. + * + **************************************/ +/* No longer attempt to handle overflow internally */ + signal(SIGFPE, system_overflow_handler); +} + + +int API_ROUTINE ISC_kill(SLONG pid, SLONG signal_number, void *object_hndl) +{ +/************************************** + * + * I S C _ k i l l ( W I N _ N T ) + * + ************************************** + * + * Functional description + * Notify somebody else. + * + **************************************/ + ULONG oldest_age; + OPN_EVENT opn_event, end_opn_event, oldest_opn_event; + +/* If we're simply trying to poke ourselves, do so directly. */ + if (!process_id) + process_id = GetCurrentProcessId(); + + if (pid == process_id) { + SetEvent(object_hndl); + return 0; + } + + oldest_age = ~0; + + opn_event = opn_events; + end_opn_event = opn_event + opn_event_count; + for (; opn_event < end_opn_event; opn_event++) { + if (opn_event->opn_event_pid == pid && + opn_event->opn_event_signal == signal_number) break; + if (opn_event->opn_event_age < oldest_age) { + oldest_opn_event = opn_event; + oldest_age = opn_event->opn_event_age; + } + } + + if (opn_event >= end_opn_event) { + HANDLE lhandle; + + if (!(lhandle = ISC_make_signal(FALSE, FALSE, pid, signal_number))) + return -1; + + if (opn_event_count < MAX_OPN_EVENTS) + opn_event_count++; + else { + opn_event = oldest_opn_event; + CloseHandle(opn_event->opn_event_lhandle); + } + + opn_event->opn_event_pid = pid; + opn_event->opn_event_signal = signal_number; + opn_event->opn_event_lhandle = lhandle; + } + + opn_event->opn_event_age = ++opn_event_clock; + + return (SetEvent(opn_event->opn_event_lhandle)) ? 0 : -1; +} + +void DLL_EXPORT ISC_signal_init(void) +{ +/************************************** + * + * I S C _ s i g n a l _ i n i t + * + ************************************** + * + * Functional description + * Initialize any system signal handlers. + * + **************************************/ + +#ifndef REQUESTER + if (initialized_signals) + return; + + initialized_signals = TRUE; + + overflow_count = 0; + gds__register_cleanup(cleanup, 0); + + process_id = getpid(); + + THD_MUTEX_INIT(&sig_mutex); + + system_overflow_handler = signal(SIGFPE, overflow_handler); + +#endif /* REQUESTER */ + + ISC_get_security_desc(); +} + + +#ifndef REQUESTER +static void cleanup(void *arg) +{ +/************************************** + * + * c l e a n u p + * + ************************************** + * + * Functional description + * Module level cleanup handler. + * + **************************************/ + signals = NULL; + + THD_MUTEX_DESTROY(&sig_mutex); + + pending_signals = 0; + + process_id = 0; + + OPN_EVENT opn_event; + + opn_event = opn_events + opn_event_count; + opn_event_count = 0; + while (opn_event-- > opn_events) + CloseHandle(opn_event->opn_event_lhandle); + + initialized_signals = FALSE; +} +#endif + +#ifndef REQUESTER +static void overflow_handler(int signal, int code) +{ +/************************************** + * + * o v e r f l o w _ h a n d l e r + * + ************************************** + * + * Functional description + * Somebody overflowed. Ho hum. + * + **************************************/ + +#ifdef DEBUG_FPE_HANDLING + ib_fprintf(ib_stderr, "overflow_handler (%x)\n", arg); +#endif + +/* If we're within ISC world (inside why-value) when the FPE occurs + * we handle it (basically by ignoring it). If it occurs outside of + * ISC world, return back a code that tells signal_handler to call any + * customer provided handler. + */ + if (isc_enter_count) { + ++overflow_count; +#ifdef DEBUG_FPE_HANDLING + ib_fprintf(ib_stderr, "SIGFPE in isc code ignored %d\n", + overflow_count); +#endif + /* We've "handled" the FPE */ + } + else { + /* We've NOT "handled" the FPE - let's chain + the signal to other handlers */ + if (system_overflow_handler != SIG_DFL && + system_overflow_handler != SIG_IGN && + system_overflow_handler != SIG_HOLD) + { + system_overflow_handler(signal, code); + } + } +} +#endif \ No newline at end of file diff --git a/src/jrd/svc.cpp b/src/jrd/svc.cpp index ea7972a912..679d03440f 100644 --- a/src/jrd/svc.cpp +++ b/src/jrd/svc.cpp @@ -206,7 +206,7 @@ static void service_fork(TEXT *, SVC); static void service_get(SVC, SCHAR *, USHORT, USHORT, USHORT, USHORT *); static void service_put(SVC, SCHAR *, USHORT); #if !defined(WIN_NT) && !defined(SUPERSERVER) -static void timeout_handler(SVC); +static void timeout_handler(void *service); #endif #if defined(WIN_NT) && !defined(SUPERSERVER) static USHORT service_read(SVC, SCHAR *, USHORT, USHORT); @@ -3116,17 +3116,8 @@ static void service_get( * that we have checked out of the scheduler. * **************************************/ -#ifdef SYSV_SIGNALS - SLONG sv_timr; - void *sv_hndlr; -#else struct itimerval sv_timr; -#ifndef HAVE_SIGACTION - struct sigvec sv_hndlr; -#else struct sigaction sv_hndlr; -#endif -#endif int c; //USHORT timed_out; SCHAR *buf; @@ -3140,7 +3131,7 @@ static void service_get( buf = buffer; if (timeout) { - ISC_set_timer((SLONG) (timeout * 100000), (void(*)())timeout_handler, service, + ISC_set_timer((SLONG) (timeout * 100000), timeout_handler, service, (SLONG*)&sv_timr, (void**)&sv_hndlr); iter = timeout * 10; } @@ -3165,7 +3156,7 @@ static void service_get( else { errno_save = errno; if (timeout) - ISC_reset_timer((void(*)())timeout_handler, service, (SLONG*)&sv_timr, + ISC_reset_timer(timeout_handler, service, (SLONG*)&sv_timr, (void**)&sv_hndlr); io_error("ib_getc", errno_save, "service pipe", isc_io_read_err, TRUE); @@ -3173,7 +3164,7 @@ static void service_get( } if (timeout) { - ISC_reset_timer((void(*)())timeout_handler, service, (SLONG*)&sv_timr, (void**)&sv_hndlr); + ISC_reset_timer(timeout_handler, service, (SLONG*)&sv_timr, (void**)&sv_hndlr); if (!iter) service->svc_flags |= SVC_timeout; } @@ -3215,7 +3206,7 @@ static void service_put(SVC service, SCHAR * buffer, USHORT length) } -static void timeout_handler(SVC service) +static void timeout_handler(void *service) { /************************************** * diff --git a/src/jrd/thd.h b/src/jrd/thd.h index 4b2bd1315a..359cc389fa 100644 --- a/src/jrd/thd.h +++ b/src/jrd/thd.h @@ -26,7 +26,7 @@ * */ /* -$Id: thd.h,v 1.13 2003-08-10 15:43:22 skidder Exp $ +$Id: thd.h,v 1.14 2003-09-08 20:23:35 skidder Exp $ */ #ifndef _JRD_THD_H_ @@ -137,8 +137,6 @@ struct IB_RTL_CRITICAL_SECTION #endif #ifdef MULTI_THREAD -#define SIGNAL_INHIBIT -#define SIGNAL_ENABLE #ifdef SUPERSERVER #define THREAD_ENTER SCH_enter() #define THREAD_EXIT SCH_exit() @@ -163,11 +161,6 @@ struct IB_RTL_CRITICAL_SECTION #define THREAD_YIELD #endif -#ifndef SIGNAL_INHIBIT -#define SIGNAL_INHIBIT ISC_inhibit() -#define SIGNAL_ENABLE ISC_enable() -#endif - /* Thread priorities (may be ignored) */ #define THREAD_high 1 diff --git a/src/jrd/why.cpp b/src/jrd/why.cpp index 90eb45ffd2..3561aa3154 100644 --- a/src/jrd/why.cpp +++ b/src/jrd/why.cpp @@ -42,7 +42,7 @@ * */ /* -$Id: why.cpp,v 1.25 2003-09-08 14:09:34 dimitr Exp $ +$Id: why.cpp,v 1.26 2003-09-08 20:23:35 skidder Exp $ */ #include "firebird.h" @@ -76,7 +76,7 @@ $Id: why.cpp,v 1.25 2003-09-08 14:09:34 dimitr Exp $ #include "../jrd/isc_proto.h" #include "../jrd/isc_f_proto.h" #ifndef REQUESTER -#include "../jrd/isc_i_proto.h" +#include "../jrd/os/isc_i_proto.h" #include "../jrd/isc_s_proto.h" #include "../jrd/sch_proto.h" #endif diff --git a/src/lock/lock.cpp b/src/lock/lock.cpp index bccbd4a9eb..b900635419 100644 --- a/src/lock/lock.cpp +++ b/src/lock/lock.cpp @@ -39,7 +39,7 @@ */ /* -$Id: lock.cpp,v 1.69 2003-09-07 00:53:59 brodsom Exp $ +$Id: lock.cpp,v 1.70 2003-09-08 20:23:39 skidder Exp $ */ #include "firebird.h" @@ -54,7 +54,7 @@ $Id: lock.cpp,v 1.69 2003-09-07 00:53:59 brodsom Exp $ #include "../jrd/gds_proto.h" #include "../jrd/gdsassert.h" #include "../jrd/isc_proto.h" -#include "../jrd/isc_i_proto.h" +#include "../jrd/os/isc_i_proto.h" #include "../jrd/isc_s_proto.h" #include "../jrd/sch_proto.h" #include "../jrd/thd_proto.h" @@ -202,7 +202,7 @@ static USHORT alloc_semaphore(OWN, ISC_STATUS *); #ifndef SUPERSERVER // This is either signal handler of called from blocking_thread // only SuperServer does direct calls to blocking_action2 -static void blocking_action(PTR); +static void blocking_action(void *_owner_offset); #endif static void blocking_action2(PTR, PTR); #ifdef USE_BLOCKING_THREAD @@ -236,7 +236,7 @@ static PTR grant_or_que(LRQ, LBL, SSHORT); static ISC_STATUS init_lock_table(ISC_STATUS *); static void init_owner_block(OWN, UCHAR, ULONG, USHORT); #ifdef USE_WAKEUP_EVENTS -static void lock_alarm_handler(EVENT); +static void lock_alarm_handler(void *event); #endif static void lock_initialize(void *, SH_MEM, int); static void insert_data_que(LBL); @@ -371,6 +371,21 @@ static const UCHAR compatibility[] = { #define COMPATIBLE(st1, st2) compatibility [st1 * LCK_max + st2] +void LOCK_ast_inhibit() { +#ifdef MULTI_THREAD + AST_DISABLE; +#else + ISC_inhibit(); +#endif +} + +void LOCK_ast_enable() { +#ifdef MULTI_THREAD + AST_ENABLE; +#else + ISC_enable(); +#endif +} int LOCK_convert(PTR request_offset, UCHAR type, @@ -752,8 +767,7 @@ void LOCK_fini( ISC_STATUS * status_vector, PTR * owner_offset) release_mutex(); #ifdef USE_BLOCKING_SIGNALS - ISC_signal_cancel(LOCK_block_signal, ( void (*)()) blocking_action, - (void *) offset); + ISC_signal_cancel(LOCK_block_signal, blocking_action, (void *)offset); #endif *owner_offset = (PTR)0; @@ -816,7 +830,7 @@ int LOCK_init( #ifdef USE_BLOCKING_SIGNALS if (LOCK_owner_offset) - ISC_signal(LOCK_block_signal, (void(*)()) blocking_action, + ISC_signal(LOCK_block_signal, blocking_action, (void *) LOCK_owner_offset); #endif @@ -1002,7 +1016,7 @@ void LOCK_manager( PTR manager_owner_offset) by setting an alarm clock. */ ret = ISC_event_wait(1, &event_ptr, &value, - LOCKMANTIMEOUT * 1000000, (FPTR_VOID) lock_alarm_handler, + LOCKMANTIMEOUT * 1000000, lock_alarm_handler, event_ptr); #ifdef DEBUG @@ -1559,7 +1573,7 @@ static void acquire( PTR owner_offset) release_mutex(); ret = ISC_event_wait(1, &event_ptr, &value, LOCK_solaris_stall * 1000000, - (void(*)()) lock_alarm_handler, event_ptr); + lock_alarm_handler, event_ptr); #ifdef DEV_BUILD if (ret != FB_SUCCESS) gds__log @@ -1747,7 +1761,7 @@ static USHORT alloc_semaphore( OWN owner, ISC_STATUS * status_vector) #ifndef SUPERSERVER -static void blocking_action( PTR owner_offset) +static void blocking_action( void* _owner_offset) { /************************************** * @@ -1771,6 +1785,7 @@ static void blocking_action( PTR owner_offset) * been done. * **************************************/ + PTR owner_offset = (PTR)(U_IPTR)_owner_offset; /* Ignore signals that occur when executing in lock manager or when there is no owner block set up */ @@ -3195,7 +3210,7 @@ static void init_owner_block( #ifdef USE_WAKEUP_EVENTS -static void lock_alarm_handler( EVENT event) +static void lock_alarm_handler(void* event) { /************************************** * @@ -3213,7 +3228,7 @@ static void lock_alarm_handler( EVENT event) * **************************************/ - ISC_event_post(event); + ISC_event_post(reinterpret_cast(event)); } #endif @@ -5034,7 +5049,7 @@ static USHORT wait_for_request( AST_ENABLE; ret = ISC_event_wait(1, &event_ptr, &value, (timeout - current_time) * 1000000, - (void(*)())lock_alarm_handler, event_ptr); + lock_alarm_handler, event_ptr); AST_DISABLE; #ifdef SUPERSERVER THREAD_ENTER; diff --git a/src/lock/lock_proto.h b/src/lock/lock_proto.h index eca9503bf0..0181abe2e3 100644 --- a/src/lock/lock_proto.h +++ b/src/lock/lock_proto.h @@ -40,5 +40,7 @@ SLONG LOCK_read_data2(SLONG, USHORT, UCHAR *, USHORT, SLONG); void LOCK_re_post(int (*)(void *), void *, SLONG); bool LOCK_shut_manager(void); SLONG LOCK_write_data(SLONG, SLONG); +void LOCK_ast_inhibit(); +void LOCK_ast_enable(); #endif /* _LOCK_LOCK_PROTO_H_ */ diff --git a/src/lock/print.cpp b/src/lock/print.cpp index 3126dffd79..77747c525b 100644 --- a/src/lock/print.cpp +++ b/src/lock/print.cpp @@ -110,7 +110,7 @@ static const UCHAR compatibility[] = { -void CLIB_ROUTINE main( int argc, char *argv[]) +int CLIB_ROUTINE main( int argc, char *argv[]) { /************************************** * diff --git a/src/remote/inet.cpp b/src/remote/inet.cpp index 8dc5d3bd02..49a1f66ed4 100644 --- a/src/remote/inet.cpp +++ b/src/remote/inet.cpp @@ -41,7 +41,7 @@ * */ /* -$Id: inet.cpp,v 1.72 2003-08-28 12:56:15 brodsom Exp $ +$Id: inet.cpp,v 1.73 2003-09-08 20:23:41 skidder Exp $ */ #include "firebird.h" #include "../jrd/ib_stdio.h" @@ -113,7 +113,7 @@ extern "C" int innetgr(const char *, const char *, const char *, const char *); #include "../jrd/gds_proto.h" #include "../jrd/isc_proto.h" #ifndef REQUESTER -#include "../jrd/isc_i_proto.h" +#include "../jrd/os/isc_i_proto.h" #include "../jrd/sch_proto.h" #endif /* REQUESTER */ @@ -367,7 +367,7 @@ static bool_t inet_getbytes(XDR *, SCHAR *, u_int); static bool_t inet_getlong(XDR *, SLONG *); static u_int inet_getpostn(XDR *); #if !(defined WIN_NT) -static void inet_handler(PORT); +static void inet_handler(void* _port); #endif static caddr_t inet_inline(XDR *, u_int); static int inet_error(PORT, const TEXT *, ISC_STATUS, int); @@ -1655,7 +1655,7 @@ static PORT aux_connect(PORT port, PACKET* packet, XDR_INT (*ast)(void)) } new_port->port_ast = ast; - ISC_signal(SIGURG, (FPTR_VOID)inet_handler, new_port); + ISC_signal(SIGURG, inet_handler, new_port); } #endif /* SIOCSPGRP */ @@ -1959,7 +1959,7 @@ static void disconnect( PORT port) #if !(defined VMS || defined WIN_NT) if (port->port_ast) { - ISC_signal_cancel(SIGURG, (FPTR_VOID) inet_handler, port); + ISC_signal_cancel(SIGURG, inet_handler, port); } #endif @@ -3087,7 +3087,7 @@ static u_int inet_getpostn( XDR * xdrs) } #if !(defined WIN_NT) -static void inet_handler( PORT port) +static void inet_handler(void *_port) { /************************************** * @@ -3103,6 +3103,7 @@ static void inet_handler( PORT port) * handler to do something appropriate. * **************************************/ + PORT port = reinterpret_cast(_port); int n; SCHAR junk; @@ -3736,11 +3737,7 @@ static bool_t packet_send( PORT port, SCHAR * buffer, SSHORT buffer_length) #ifdef HAVE_SETITIMER struct itimerval internal_timer, client_timer; -#ifdef HAVE_SIGACTION struct sigaction internal_handler, client_handler; -#else - struct sigvec internal_handler, client_handler; -#endif #endif /* HAVE_SETITIMER */ data = buffer; @@ -3808,18 +3805,10 @@ static bool_t packet_send( PORT port, SCHAR * buffer, SSHORT buffer_length) internal_timer.it_value.tv_sec = 0; internal_timer.it_value.tv_usec = 0; setitimer(ITIMER_REAL, &internal_timer, &client_timer); -#ifndef HAVE_SIGACTION - internal_handler.sv_handler = alarm_handler; - internal_handler.sv_mask = 0; - internal_handler.sv_flags = SV_INTERRUPT; - sigvector(SIGALRM, &internal_handler, &client_handler); -#else - internal_handler.sa_handler = (SIG_FPTR)alarm_handler; - memset(&internal_handler.sa_mask, 0, - sizeof(internal_handler.sa_mask)); + internal_handler.sa_handler = alarm_handler; + sigemptyset(&internal_handler.sa_mask); internal_handler.sa_flags = SA_RESTART; sigaction(SIGALRM, &internal_handler, &client_handler); -#endif } internal_timer.it_value.tv_sec = 0; @@ -3835,11 +3824,7 @@ static bool_t packet_send( PORT port, SCHAR * buffer, SSHORT buffer_length) internal_timer.it_value.tv_sec = 0; internal_timer.it_value.tv_usec = 0; setitimer(ITIMER_REAL, &internal_timer, NULL); -#ifdef HAVE_SIGACTION sigaction(SIGALRM, &client_handler, NULL); -#else - sigvector(SIGALRM, &client_handler, NULL); -#endif setitimer(ITIMER_REAL, &client_timer, NULL); } #endif /* HAVE_SETITIMER */ diff --git a/src/remote/inet_server.cpp b/src/remote/inet_server.cpp index ebf2e9b65e..a5acefdc4a 100644 --- a/src/remote/inet_server.cpp +++ b/src/remote/inet_server.cpp @@ -32,7 +32,7 @@ * */ /* -$Id: inet_server.cpp,v 1.27 2003-08-10 01:22:12 brodsom Exp $ +$Id: inet_server.cpp,v 1.28 2003-09-08 20:23:41 skidder Exp $ */ #include "firebird.h" #include "../jrd/ib_stdio.h" @@ -154,11 +154,11 @@ extern "C" { static int assign(SCHAR *); #endif //static void name_process(UCHAR *); -static void signal_handler(void); +static void signal_handler(int); #ifdef SUPERSERVER -static void signal_sigpipe_handler(void); +static void signal_sigpipe_handler(int); #endif -static void set_signal(int, FPTR_VOID); +static void set_signal(int, void (*)(int)); #ifdef WINDOWS_ROUTER static int atov(UCHAR *, UCHAR **, SSHORT); @@ -195,6 +195,14 @@ int CLIB_ROUTINE main( int argc, char **argv) fd_set mask; #endif +// 01 Sept 2003, Nickolay Samofatov +// In GCC version 3.1-3.3 we need to install special error handler +// in order to get meaningful terminate() error message on stderr. +// In GCC 3.4 or later this is the default. +#if __GNUC__ == 3 && __GNUC_MINOR__ >= 1 && __GNUC_MINOR__ < 4 + std::set_terminate (__gnu_cxx::__verbose_terminate_handler); +#endif + #ifdef WINDOWS_ROUTER /* * Construct an argc, argv so we can use the old parse code. @@ -340,7 +348,7 @@ int CLIB_ROUTINE main( int argc, char **argv) } gds__log("INET_SERVER/main: gds_inet_server restarted"); } - set_signal(SIGUSR1, (void(*)()) SIG_DFL); + set_signal(SIGUSR1, SIG_DFL); } #endif @@ -559,7 +567,7 @@ static int assign( SCHAR * string) #if !(defined VMS) -static void set_signal( int signal_number, void (*handler) (void)) +static void set_signal( int signal_number, void (*handler) (int)) { /************************************** * @@ -571,33 +579,17 @@ static void set_signal( int signal_number, void (*handler) (void)) * Establish signal handler. * **************************************/ -#ifdef SYSV_SIGNALS - sigset(signal_number, handler); -#else - -#ifndef HAVE_SIGACTION - struct sigvec vec; - struct sigvec old_vec; - - vec.sv_handler = handler; - vec.sv_mask = 0; - vec.sv_flags = SV_INTERRUPT; - sigvector(signal_number, &vec, &old_vec); -#else struct sigaction vec, old_vec; - vec.sa_handler = (SIG_FPTR) handler; - memset(&vec.sa_mask, 0, sizeof(vec.sa_mask)); + vec.sa_handler = handler; + sigemptyset(&vec.sa_mask); vec.sa_flags = 0; sigaction(signal_number, &vec, &old_vec); -#endif - -#endif } #endif -static void signal_handler(void) +static void signal_handler(int) { /************************************** * @@ -614,7 +606,7 @@ static void signal_handler(void) } #if (defined SUPERSERVER && defined UNIX ) -static void signal_sigpipe_handler(void) +static void signal_sigpipe_handler(int) { /**************************************************** * diff --git a/src/remote/os/win32/srvr_w32.cpp b/src/remote/os/win32/srvr_w32.cpp index 9893f29ef5..44bf9e928b 100644 --- a/src/remote/os/win32/srvr_w32.cpp +++ b/src/remote/os/win32/srvr_w32.cpp @@ -104,7 +104,7 @@ #include "../jrd/isc_proto.h" #include "../jrd/thd_proto.h" #include "../jrd/jrd_proto.h" -#include "../jrd/isc_i_proto.h" +#include "../jrd/os/isc_i_proto.h" #include "../jrd/isc_s_proto.h" #include "../jrd/file_params.h" #include "../common/config/config.h" diff --git a/src/remote/server.cpp b/src/remote/server.cpp index 08688d8a56..ae18a2190d 100644 --- a/src/remote/server.cpp +++ b/src/remote/server.cpp @@ -56,7 +56,7 @@ #include "gen/codes.h" #endif #ifdef SUPERSERVER -#include "../jrd/isc_i_proto.h" +#include "../jrd/os/isc_i_proto.h" #endif #include "../remote/proto_proto.h" // xdr_protocol_overhead() diff --git a/src/utilities/gstat/ppg.cpp b/src/utilities/gstat/ppg.cpp index 4bfd9991f2..749f9a2aa9 100644 --- a/src/utilities/gstat/ppg.cpp +++ b/src/utilities/gstat/ppg.cpp @@ -31,6 +31,8 @@ #include "../jrd/jrd_time.h" #include "../jrd/gds.h" #include "../jrd/ods.h" +#include "../jrd/os/guid.h" +#include "../jrd/nbak.h" #include "../jrd/gds_proto.h" static const TEXT months[][4] = { @@ -181,6 +183,17 @@ void PPG_print_header( HDR header, SLONG page, FPRINTF(outfile, ", "); FPRINTF(outfile, "read only"); } + if (flags & hdr_backup_mask) { + if (flag_count++) + FPRINTF(outfile, ", "); + if ((flags & hdr_backup_mask) == nbak_state_stalled) + FPRINTF(outfile, "backup lock"); + else + if ((flags & hdr_backup_mask) == nbak_state_merge) + FPRINTF(outfile, "backup merge"); + else + FPRINTF(outfile, "wrong backup state %d", flags & hdr_backup_mask); + } FPRINTF(outfile, "\n"); } @@ -229,9 +242,24 @@ void PPG_print_header( HDR header, SLONG page, break; case HDR_cache_file: - FPRINTF(outfile, "\tShared Cache file:\t\t%s\n", p + 2); + memcpy(temp, p + 2, p[1]); + temp[p[1]] = '\0'; + FPRINTF(outfile, "\tShared Cache file:\t\t%s\n", temp); break; + case HDR_difference_file: + memcpy(temp, p + 2, p[1]); + temp[p[1]] = '\0'; + FPRINTF(outfile, "\tBackup difference file:\t%s\n", temp); + break; + + case HDR_backup_guid: { + char buff[GUID_BUFF_SIZE]; + GuidToString(buff, reinterpret_cast(p+2)); + FPRINTF(outfile, "\tDatabase backup GUID:\t%s\n", buff); + break; + } + default: if (*p > HDR_max) FPRINTF(outfile, "\tUnrecognized option %d, length %d\n", diff --git a/src/utilities/nbackup.cpp b/src/utilities/nbackup.cpp index 16759530f7..528d2a3ff6 100644 --- a/src/utilities/nbackup.cpp +++ b/src/utilities/nbackup.cpp @@ -3,24 +3,37 @@ * MODULE: nbackup.cpp * DESCRIPTION: Command line utility for physical backup/restore * - * The contents of this file are subject to the Interbase Public - * License Version 1.0 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy - * of the License at http://www.Inprise.com/IPL.html + * This library 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; either + * version 2.1 of the License, or (at your option) any later version. + * You may obtain a copy of the Licence at + * http://www.gnu.org/licences/lgpl.html + * + * As a special exception this file can also be included in modules + * with other source code as long as that source code has been + * released under an Open Source Initiative certificed licence. + * More information about OSI certification can be found at: + * http://www.opensource.org + * + * This module 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 Licence for more details. + * + * This module was created by members of the firebird development + * team. All individual contributions remain the Copyright (C) of + * those individuals and all rights are reserved. Contributors to + * this file are either listed below or can be obtained from a CVS + * history command. * - * Software distributed under the License is distributed on an - * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express - * or implied. See the License for the specific language governing - * rights and limitations under the License. + * Created by: Nickolay Samofatov * - * The Original Code was created by Inprise Corporation - * and its predecessors. Portions created by Inprise Corporation are - * Copyright (C) Inprise Corporation. + * Contributor(s): + * * - * Created by: Nickolay Samofatov + * $Id: nbackup.cpp,v 1.6 2003-09-08 20:23:43 skidder Exp $ * - * All Rights Reserved. - * Contributor(s): ______________________________________. */ @@ -38,6 +51,7 @@ #include "../jrd/os/path_utils.h" #include "../jrd/os/guid.h" #include "../jrd/ibase.h" +#include #ifdef HAVE_UNISTD_H #include @@ -100,6 +114,7 @@ public: #else vsprintf(temp, message, params); #endif + fprintf(stderr, "Failure: %s\n", temp); va_end(params); throw b_error(temp); } @@ -390,6 +405,10 @@ void nbackup::attach_database() { } void nbackup::detach_database() { + if (trans) { + if (isc_rollback_transaction(status, &trans)) + pr_error(status, "rollback transaction"); + } if (isc_detach_database(status, &newdb)) pr_error(status, "detach database"); } @@ -418,8 +437,11 @@ void nbackup::lock_database() { attach_database(); try { internal_lock_database(); - } catch(const std::exception&) { - detach_database(); + } catch(const std::exception& ex) { + if (typeid(ex) != typeid(b_error)) { + fprintf(stderr, "Unexpected error %s: %s\n", typeid(ex).name(), ex.what()); + } + detach_database(); throw; } detach_database(); @@ -429,8 +451,11 @@ void nbackup::unlock_database() { attach_database(); try { internal_unlock_database(); - } catch(const std::exception&) { - detach_database(); + } catch(const std::exception& ex) { + if (typeid(ex) != typeid(b_error)) { + fprintf(stderr, "Unexpected error %s: %s\n", typeid(ex).name(), ex.what()); + } + detach_database(); throw; } detach_database(); @@ -579,7 +604,8 @@ void nbackup::backup_database(int level, const char* fname) { ULONG curPage = 0; while (true) { if (curPage && page_buff->pag_scn() > backup_scn) - b_error::raise("Internal error. Database had been changed during backup"); + b_error::raise("Internal error. Database page %d had been changed during backup" + " (page SCN=%d, backup SCN=%d)", curPage, page_buff->pag_scn(), backup_scn); if (level) { if (page_buff->pag_scn() > prev_scn) { write_file(backup, &curPage, sizeof(curPage)); @@ -642,7 +668,10 @@ void nbackup::backup_database(int level, const char* fname) { if (isc_commit_transaction(status, &trans)) pr_error(status, "commit history insert"); - } catch (const std::exception&) { + } catch (const std::exception& ex) { + if (typeid(ex) != typeid(b_error)) { + fprintf(stderr, "Unexpected error %s: %s\n", typeid(ex).name(), ex.what()); + } free(page_buff); if (delete_backup) unlink(bakname); @@ -793,7 +822,10 @@ void nbackup::restore_database(int filecount, char* files[]) { close_backup(); curLevel++; } - } catch(const std::exception&) { + } catch(const std::exception& ex) { + if (typeid(ex) != typeid(b_error)) { + fprintf(stderr, "Unexpected error %s: %s\n", typeid(ex).name(), ex.what()); + } free(page); if (delete_database) unlink(dbname); @@ -855,7 +887,7 @@ int main( int argc, char *argv[] ) } } catch (const std::exception& ex) { - fprintf(stderr, "Failure: %s\n", ex.what()); + // It must have been printed out. No need to repeat the task return EXIT_ERROR; } diff --git a/src/wal/wal.cpp b/src/wal/wal.cpp index 6e86954454..94acfe3e19 100644 --- a/src/wal/wal.cpp +++ b/src/wal/wal.cpp @@ -1279,7 +1279,7 @@ static SSHORT grpc_wait_for_grouping( &ptr, &value, WAL_handle->wal_grpc_wait_usecs, - reinterpret_cast < void (*)() > (WALC_alarm_handler), ptr); + WALC_alarm_handler, ptr); /* Now make sure that the other group-commit block is available */ @@ -1292,8 +1292,7 @@ static SSHORT grpc_wait_for_grouping( ptr = &WAL_EVENTS[WAL_GCOMMIT_STALL_SEM]; ISC_event_wait(1, &ptr, &value, WAL_handle->wal_grpc_wait_other_coord_usecs, - reinterpret_cast < void (*)() > (WALC_alarm_handler), - ptr); + WALC_alarm_handler, ptr); WALC_acquire(WAL_handle, &WAL_segment); WAL_CHECK_BUG(WAL_handle, WAL_segment); if ((WAL_segment->wals_flags & WALS_GRP_COMMIT_IN_PROGRESS) && @@ -1359,10 +1358,10 @@ GRP_COMMIT * grpc) ptr = &WAL_EVENTS[grpc->grp_commit_event_num]; value = ISC_event_clear(ptr); WALC_release(WAL_handle); - while (ISC_event_wait - (1, &ptr, &value, WAL_handle->wal_grpc_wait_coord_usecs, - reinterpret_cast < void (*)() > (WALC_alarm_handler), - ptr) != FB_SUCCESS) { + while (ISC_event_wait(1, &ptr, &value, + WAL_handle->wal_grpc_wait_coord_usecs, + WALC_alarm_handler, ptr) != FB_SUCCESS) + { /* Check to make sure that the coordinator is still alive. */ WALC_acquire(WAL_handle, &WAL_segment); @@ -1576,8 +1575,7 @@ static SSHORT wait_for_writer( ISC_STATUS * status_vector, WAL WAL_handle) ret = ISC_event_wait(1, &ptr, &value, WAIT_TIME, - reinterpret_cast < void (*)() > (WALC_alarm_handler), - ptr); + WALC_alarm_handler, ptr); if (ret == FB_FAILURE) { /* We got out because of timeout. May be our condition is already met. Let the caller decide that. In any case, make diff --git a/src/wal/walc.cpp b/src/wal/walc.cpp index 753eefc4a5..70aa495c0e 100644 --- a/src/wal/walc.cpp +++ b/src/wal/walc.cpp @@ -41,7 +41,7 @@ #include "../jrd/gds_proto.h" #include "../jrd/iberr_proto.h" #include "../jrd/isc_proto.h" -#include "../jrd/isc_i_proto.h" +#include "../jrd/os/isc_i_proto.h" #include "../jrd/isc_s_proto.h" #include "../jrd/llio_proto.h" @@ -177,7 +177,7 @@ void WALC_acquire( WAL WAL_handle, WALS * address) } -void WALC_alarm_handler( EVENT event) +void WALC_alarm_handler(void* _event) { /************************************** * @@ -195,7 +195,7 @@ void WALC_alarm_handler( EVENT event) * **************************************/ - ISC_event_post(event); + ISC_event_post(reinterpret_cast(_event)); } diff --git a/src/wal/walc_proto.h b/src/wal/walc_proto.h index e716e64d92..00440f8f71 100644 --- a/src/wal/walc_proto.h +++ b/src/wal/walc_proto.h @@ -25,7 +25,7 @@ #define _WAL_WALC_PROTO_H_ extern void WALC_acquire (struct wal *, struct wals **); -extern void WALC_alarm_handler (struct event *); +extern void WALC_alarm_handler (void *); extern SSHORT WALC_bug (ISC_STATUS *, TEXT *, TEXT *); extern void WALC_build_dbg_filename (TEXT *, TEXT *); extern void WALC_build_logname (TEXT *, TEXT *, SLONG); diff --git a/src/wal/walw.cpp b/src/wal/walw.cpp index 7876bef9e8..3841a64213 100644 --- a/src/wal/walw.cpp +++ b/src/wal/walw.cpp @@ -47,7 +47,7 @@ #include "../wal/walw_proto.h" #include "../jrd/gds_proto.h" #include "../jrd/iberr_proto.h" -#include "../jrd/isc_i_proto.h" +#include "../jrd/os/isc_i_proto.h" #include "../jrd/isc_s_proto.h" #include "../jrd/jrn_proto.h" #include "../jrd/llio_proto.h" @@ -493,8 +493,7 @@ static SSHORT WALW_writer(ISC_STATUS * status_vector, WAL WAL_handle) WALC_release(WAL_handle); acquired = false; ISC_event_wait(1, &ptr, &value, WALW_WRITER_TIMEOUT_USECS, - reinterpret_cast < void (*)() > - (WALC_alarm_handler), ptr); + WALC_alarm_handler, ptr); continue; }