1 /* ***********************************************************
  2    *                                                         *
  3    *                                                         *
  4    * Copyright, (C) Honeywell Information Systems Inc., 1981 *
  5    *                                                         *
  6    *                                                         *
  7    *********************************************************** */
  8 
  9 /* ******************************************************
 10    *                                                    *
 11    *                                                    *
 12    * Copyright (c) 1972 by Massachusetts Institute of   *
 13    * Technology and Honeywell Information Systems, Inc. *
 14    *                                                    *
 15    *                                                    *
 16    ****************************************************** */
 17 
 18 mu_quiesce: proc;
 19 
 20 /* HISTORY:
 21 
 22    Originally written by Al Kepner, March 1979.
 23 
 24    80-12-09 Jim Gray : changed method of getting dbc_ptr, so that a common
 25    routine could determine if AMDB -rs needed to be run to update the
 26    dbc structure versions.
 27 
 28    82-05-19 Mike Kubicar : modified to allow multiple database quiesces
 29    per process.  Also removed code that started to implement multiple
 30    quiesces per call.
 31 
 32    84-10-23 Paul Benjamin : changed to use a smaller lock wait time when
 33    calling set_lock_$lock from the cleanup handler.
 34 */
 35 ^L
 36 /* DESCRIPTION:
 37 
 38 
 39    BEGIN_DESCRIPTION
 40    " N^H__^Ha_^Hm_^He:  mu_quiesce
 41    "
 42    "      This  routine allows  the caller to  quiesce an  entire data
 43    " base   or  selected   files   for  such   purposes   as  dumping,
 44    " restructuring, or file ring changes. Files which are quiesced are
 45    " reserved  for the  exclusive use  of the  process which  does the
 46    " quiescing.
 47    "
 48    "
 49    " E^H__^Hn_^Ht_^Hr_^Hy:  mu_quiesce$quiesce_quiet
 50    "
 51    "
 52    "      This  entry is  called  to quiesce  an entire  data  base or
 53    " selected files.
 54    "
 55    "
 56    " U^H__^Hs_^Ha_^Hg_^He
 57    "
 58    "      dcl  mu_quiesce$quiesce_quiet entry (char (168), fixed bin (17),
 59    "           fix bin (35));
 60    "
 61    "      call mu_quiesce$quiesce_quiet (database_pathname, wait_time, code);
 62    "
 63    " where:
 64    "
 65    " 1.   database_pathname    (Input)
 66    "             the pathname of the database to be quiesced.
 67    "
 68    " 2.   wait_time            (Input)
 69    "             the length of time (in seconds) to wait on locks.
 70    "
 71    " 3.   code                 (Output)
 72    "             is a standard system error code.  If the wait time is
 73    "             exceeded   the   error    code   returned   will   be
 74    "             mdbm_error_$db_busy.  The data base has been quiesced
 75    "             only  if a  0 error  code  is returned.   Other error
 76    "             codes     which    may     be     returned    include
 77    "             mdbm_error_$quiesced_dead_db (The data  base has been
 78    "             quiesced  by  a  process  which  no  longer  exists,)
 79    "             mdbm_error_$my_quiesced_db (Attempt to quiesce a data
 80    "             base  which   has  already  been   quiesced  by  this
 81    "             process,)  mdbm_error_$hold_quiesced_db  (Attempt  to
 82    "             quiesce a  data base before  previously quiesced data
 83    "             bases  have  been  freed,)
 84    "             mdbm_error_$trouble_lock (The data base is locked and
 85    "             may          be          inconsistent.)           and
 86    "             mdbm_error_$quiesce_too_few (The number of data bases
 87    "             to quiesce is negative or zero.)
 88    "
 89    "
 90    " N^H__^Ho_^Ht_^He_^Hs
 91    "
 92    "      Only one user  at a time may quiesce a given
 93    " data base.  The  data base is locked against  further attempts to
 94    " quiesce  until  mu_quiesce$quiesce_free  is  called by  the  same
 95    " process which first called mu_quiesce$quiesce_quiet.
 96    "
 97    "
 98    " E^H__^Hn_^Ht_^Hr_^Hy:  mu_quiesce$quiesce_free
 99    "
100    "
101    "      This entry  is called to  free the files or  data base which
102    " have been quiesced.
103    "
104    "
105    " U^H__^Hs_^Ha_^Hg_^He
106    "
107    "      dcl  mu_quiesce$quiesce_free entry (char (168), fixed bin(35));
108    "
109    "      call mu_quiesce$quiesce_free (database_pathname, code);
110    "
111    " where:
112    "
113    " 1.   database_pathname    (Input)
114    "             the pathname of the database to be freed.
115    "
116    " 2.   code                 (Output)
117    "             is a  standard system error code.   Error codes which
118    "             may be returned  include mdbm_error_$quiesced_db (The
119    "             data  base  has been  quiesced  by another  process,)
120    "             mdbm_error_$quiesced_dead_db (The data  base has been
121    "             quiesced  by  a  process  which  no  longer  exists,)
122    "             mdbm_error_$trouble_lock (The data base is locked and
123    "             may          be           inconsistent,)          and
124    "             mdbm_error_$free_not_quiesced (Attempt to free a data
125    "             base which was not quiesced.)
126    "
127    "
128    END_DESCRIPTION
129 
130 */
131 ^L
132 quiesce_quiet: quiet: entry (db_path, quiesce_wait_time, code);
133           dcl     db_path                char (168);
134           dcl     quiesce_wait_time      fixed bin (17);
135           dcl     code                   fixed bin (35) parm;
136 
137 /* Initialization: */
138           hold_ul_ptr, dbc_ptr = null ();
139           handling_a_cleanup = "0"b;
140           on cleanup begin;
141                handling_a_cleanup = "1"b;
142                call tidy_up;
143           end;
144           code = 0;
145 
146 /* Obtain a pointer to the dbc (dbc_ptr). */
147           call get_dbc_ptr (bc, dbc_ptr);
148 
149 /* Lock the db against other attempts to quiesce. */
150           call set_lock_$lock (dbc.quiesce_lock,
151                quiesce_wait_time,
152                icode);
153           if icode ^= 0 then do;
154                     if icode = error_table_$lock_wait_time_exceeded
155                     then call error (mdbm_error_$db_busy);
156                     if icode = error_table_$invalid_lock_reset
157                     then do;
158                               dbc.trouble_switch = "1"b;
159                               call error (mdbm_error_$quiesced_dead_db);
160                          end;
161                     if icode = error_table_$locked_by_this_process
162                     then call error (mdbm_error_$my_quiesced_db);
163                     call error (icode);
164                end;
165 
166           time_remaining = quiesce_wait_time;
167           go to check_opens;
168 
169           do while (time_remaining > 0);
170 
171 /* Sleep for 30 seconds. */
172                time_remaining = time_remaining - 30;
173                call timer_manager_$sleep (30, "11"b);
174 
175 check_opens:   ;
176 
177 /* Lock open_lock using the wait time suppied. */
178                call set_lock_$lock (dbc.open_lock,
179                     quiesce_wait_time,
180                     icode);
181                if icode ^= 0 then do;
182                          if icode = error_table_$lock_wait_time_exceeded
183                          then call error (mdbm_error_$db_busy);
184                          if icode = error_table_$invalid_lock_reset
185                          then do;
186                                    dbc.trouble_switch = "1"b;
187                                    call error (mdbm_error_$trouble_lock);
188                               end;
189                          else call error (icode);
190                     end;
191 
192 /* Check for potentially inconsistent data base. */
193                if dbc.trouble_switch then do;
194                          call set_lock_$unlock (dbc.open_lock, icode);
195                          call error (mdbm_error_$trouble_lock);
196                     end;
197 
198 /* Determine if any users have the db open. */
199                if dbc.open_users = 0 then go to quiesce_db_ok;
200 
201 /* Unlock open_lock. */
202                call set_lock_$unlock (dbc.open_lock, icode);
203                if icode ^= 0 then call error (icode);
204           end;
205 
206 /* The wait time has been exceeded. */
207           call error (mdbm_error_$db_busy);
208 
209 quiesce_db_ok: ;
210           quiesce_sw = "1"b;
211           quiesce_db = "1"b;
212 
213 /* Add information about the quiescing process to the users list. */
214           num_filns = 0;
215           allocate user_list in (dbc.static_area) set (hold_ul_ptr);
216           ul_ptr = hold_ul_ptr;
217           call get_lock_id_ (user_list.db_lock_id);
218           if icode ^= 0 then call error (icode);
219           user_list.rdbi_bits = "0"b;
220           user_list.num_filns = 0;
221           user_list.fil_list_ofs = NULL_OFS;
222           user_list.next_active_ofs = NULL_OFS;
223           user_list.next_waiting_ofs = NULL_OFS;
224           user_list.next_open_ofs = dbc.open_users_ofs;     /* Add user to list of data base
225                                                                open users. */
226           dbc.open_users_ofs = rel (ul_ptr);
227           user_list.group_id = get_group_id_ ();
228           user_list.open_mode = mdbm_data_$quiesce_mode;
229           user_list.bypass_count = 0;
230           user_list.allowance_count = 0;
231           user_list.process_id = get_process_id_ ();
232           user_list.ev_chn_id = 0;
233           user_list.dead_proc = "0"b;
234           user_list.dead_proc_conflict = "0"b;
235           user_list.priority_high = "0"b;
236           user_list.waiting_sw = "0"b;
237           user_list.active_sw = "0"b;
238           user_list.event_signal_sw = "0"b;
239           user_list.passive_sw = "1"b;                      /* assume passivity */
240           call set_lock_$unlock (dbc.open_lock, icode);
241           if icode ^= 0 then call error (icode);
242           go to common_exit;
243 ^L
244 quiesce_free: free: entry (db_path, code);
245 
246 /* Initialization: */
247           hold_ul_ptr, dbc_ptr = null ();
248           handling_a_cleanup = "0"b;
249           on cleanup begin;
250                handling_a_cleanup = "1"b;
251                call tidy_up;
252           end;
253           code = 0;
254 
255 /* Obtain a pointer to the dbc (dbc_ptr). */
256           call get_dbc_ptr (bc, dbc_ptr);
257 
258 /* Make sure quiesce_lock was locked by this process.
259    If not give an appropriate error code and return. */
260 
261           call get_lock_id_ (lock_id);
262           if dbc.quiesce_lock ^= lock_id
263           then do;
264                     call set_lock_$lock (dbc.quiesce_lock,
265                          0, icode);
266                     if icode = 0
267                     then do;
268                               call free_db;
269                               call error (mdbm_error_$free_not_quiesced);
270                          end;
271                     else if icode = error_table_$invalid_lock_reset
272                     then do;
273                               dbc.trouble_switch = "1"b;
274                               call error (mdbm_error_$quiesced_dead_db);
275                          end;
276                     else if icode = error_table_$lock_wait_time_exceeded
277                     then call error (mdbm_error_$quiesced_db);
278                     else call error (icode);
279                end;
280           else do;
281                     call free_db;
282                end;
283 common_exit: ;
284           return;
285 ^L
286 convert: proc (a_ptr, ofs) returns (ptr);
287 
288 /* this procedure function converts an offset from NULL_OFS to null
289    or from the offset value to a pointer value within the segment denoted by a_ptr
290 */
291 
292           dcl     result                 ptr;               /* the resultant pointer value */
293           dcl     a_ptr                  ptr;               /* ptr to the segment to which the offset refers */
294           dcl     ofs                    bit (18) unal;     /* the bit offset */
295 
296           dcl     (null, ptr)            builtin;
297 
298           if ofs ^= NULL_OFS
299           then result = ptr (a_ptr, ofs);
300           else result = null;
301 
302           return (result);
303 
304      end convert;
305 ^L
306 delete_quiesce_user_from_list: proc;
307 
308 /* This routine deletes the quiesce user entry out of the list of open
309    users.  It is called in response to (1) most errors, (2) the cleanup condition,
310    and (3) the mu_quiesce$free entry point. */
311 
312           previous_ul_ptr = null ();
313           proc_id = get_process_id_ ();
314           do ul_ptr = convert (dbc_ptr, dbc.open_users_ofs)
315                repeat convert (dbc_ptr, user_list.next_open_ofs)
316                while (ul_ptr ^= null ());
317                if user_list.process_id = proc_id
318                     & user_list.open_mode = mdbm_data_$quiesce_mode
319                then do;
320                          hold_ul_ptr = ul_ptr;
321                          if previous_ul_ptr = null ()
322                          then dbc.open_users_ofs = user_list.next_open_ofs;
323                          else previous_ul_ptr -> user_list.next_open_ofs = user_list.next_open_ofs;
324                          go to free_user_storage;
325                          dcl     proc_id                bit (36); /* process_id of this process. */
326                          dcl     previous_ul_ptr        ptr;/* ptr to the user_list entry which precedes the current one */
327                     end;
328                previous_ul_ptr = ul_ptr;
329           end;
330 free_user_storage: ;
331           if hold_ul_ptr ^= null ()
332           then free hold_ul_ptr -> user_list in (dbc.static_area);
333           return;
334      end delete_quiesce_user_from_list;
335 ^L
336 error: proc (cd);
337           dcl     cd                     fixed bin (35) parm;
338           code = cd;
339           call tidy_up;
340           go to common_exit;
341      end error;
342 ^K^K
343 free_db: proc;
344           call set_lock_$lock (dbc.open_lock,
345                mdbm_data_$lock_wait, icode);
346           if icode ^= 0 then do;
347                     if icode = error_table_$lock_wait_time_exceeded
348                     then call error (mdbm_error_$db_busy);
349                     if icode = error_table_$invalid_lock_reset
350                     then do;
351                               dbc.trouble_switch = "1"b;
352                               call error (mdbm_error_$trouble_lock);
353                          end;
354                     else call error (icode);
355                end;
356 
357 /* Check for potentially inconsistent data base. */
358           if dbc.trouble_switch then do;
359                     call set_lock_$unlock (dbc.open_lock, icode);
360                     call error (mdbm_error_$trouble_lock);
361                end;
362           quiesce_sw = "0"b;
363           quiesce_db = "0"b;
364           call delete_quiesce_user_from_list;
365           call set_lock_$unlock (dbc.open_lock, icode);
366           if icode ^= 0 then call error (icode);
367           call set_lock_$unlock (dbc.quiesce_lock, icode);
368           if icode ^= 0 then call error (icode);
369           return;
370      end free_db;
371 ^L
372 get_dbc_ptr: proc (bc, dbc_ptr);
373           dcl     bc                     fixed bin (24) parm;
374           dcl     dbc_ptr                ptr parm;
375 
376 /* Use the data base pathname supplied in the quiesce data structure to
377    obtain a pointer to the dbc. */
378 
379           call mu_concurrency_control$open_control_segment (db_path,
380                dbc_ptr, bc, icode);
381           if icode ^= 0 then
382                call error (icode);
383 
384           return;
385      end get_dbc_ptr;
386 ^L
387 tidy_up: proc;
388 
389 /* This routine is called when any error occurs and for the
390    cleanup condition. It attempts to leave the dbc in a non quiesced state. */
391           if code = mdbm_error_$my_quiesced_db then return;
392           if code = mdbm_error_$hold_quiesced_db then return;
393           if code = mdbm_error_$quiesce_too_few then return;
394           if dbc_ptr = null () then return;
395           call get_lock_id_ (lock_id);
396           if dbc.quiesce_lock ^= lock_id
397           then return;
398           if handling_a_cleanup
399                then call set_lock_$lock (dbc.open_lock,     /* mdbm_data_$lock_wait is too */
400                mdbm_data_$cleanup_lock_wait, icode);        /* long for a cleanup handler */
401           else call set_lock_$lock (dbc.open_lock,
402                mdbm_data_$lock_wait, icode);
403           if icode = 0 | icode = error_table_$locked_by_this_process
404           then do;
405                     quiesce_sw = "0"b;
406                     quiesce_db = "0"b;
407                     call delete_quiesce_user_from_list;
408                     call set_lock_$unlock (dbc.open_lock, icode);
409                     call set_lock_$unlock (dbc.quiesce_lock, icode);
410                end;
411      end tidy_up;
412 ^L
413 /* VARIABLES FOR MU_QUIESCE */
414           dcl     bc                     fixed bin (24);
415           dcl     cleanup                condition;
416           dcl     error_table_$invalid_lock_reset ext fixed bin (35);
417           dcl     error_table_$lock_wait_time_exceeded ext fixed bin (35);
418           dcl     error_table_$locked_by_this_process ext fixed bin (35);
419           dcl     get_group_id_          entry returns (char (32));
420           dcl     get_lock_id_           entry (bit (36) aligned);
421           dcl     get_process_id_        entry returns (bit (36));
422           dcl     handling_a_cleanup     bit (1) aligned;
423           dcl     hold_ul_ptr            ptr;               /* ptr to user_list entry when newly allocated
424                                                                or about to be freed. */
425           dcl     icode                  fixed bin (35);
426           dcl     lock_id                bit (36) aligned;
427           dcl     mdbm_data_$cleanup_lock_wait ext fixed bin (17);
428           dcl     mdbm_data_$lock_wait   ext fixed bin (17);
429           dcl     mdbm_data_$quiesce_mode ext fixed bin (17);
430           dcl     mdbm_error_$db_busy    ext fixed bin (35);
431           dcl     mdbm_error_$free_not_quiesced ext fixed bin (35);
432           dcl     mdbm_error_$hold_quiesced_db ext fixed bin (35);
433           dcl     mdbm_error_$my_quiesced_db ext fixed bin (35);
434           dcl     mdbm_error_$quiesce_too_few ext fixed bin (35);
435           dcl     mdbm_error_$quiesced_db ext fixed bin (35);
436           dcl     mdbm_error_$quiesced_dead_db ext fixed bin (35);
437           dcl     mdbm_error_$trouble_lock ext fixed bin (35);
438           dcl     set_lock_$lock         entry (bit (36) aligned, fixed bin, fixed bin (35));
439           dcl     set_lock_$unlock       entry (bit (36) aligned, fixed bin (35));
440           dcl     sys_info$max_seg_size  ext fixed bin (35);
441           dcl     time_remaining         fixed bin (71);
442           dcl     timer_manager_$sleep   entry (fixed bin (71), bit (2));
443           dcl     mu_concurrency_control$open_control_segment entry (char (168), ptr, fixed bin (24), fixed bin (35)); /* opens dbc_ptr */
444           dcl     (fixed, null, rel) builtin;
445 ^L
446 %include mdbm_dbc;
447 ^L
448 %include mdbm_users;
449      end mu_quiesce;