source: trunk/third/evolution-data-server/libdb/dbreg/dbreg.c @ 21189

Revision 21189, 11.1 KB checked in by ghudson, 20 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r21188, which included commits to RCS files with non-trunk default branches.
Line 
1/*-
2 * See the file LICENSE for redistribution information.
3 *
4 * Copyright (c) 1996-2002
5 *      Sleepycat Software.  All rights reserved.
6 */
7#include "db_config.h"
8
9#ifndef lint
10static const char revid[] = "$Id: dbreg.c,v 1.1.1.1 2004-12-17 17:26:58 ghudson Exp $";
11#endif /* not lint */
12
13#ifndef NO_SYSTEM_INCLUDES
14#include <sys/types.h>
15
16#include <string.h>
17#endif
18
19#include "db_int.h"
20#include "dbinc/log.h"
21#include "dbinc/txn.h"
22
23/*
24 * The dbreg subsystem, as its name implies, registers database handles so
25 * that we can associate log messages with them without logging a filename
26 * or a full, unique DB ID.  Instead, we assign each dbp an int32_t which is
27 * easy and cheap to log, and use this subsystem to map back and forth.
28 *
29 * Overview of how dbreg ids are managed:
30 *
31 * OPEN
32 *      dbreg_setup (Creates FNAME struct.)
33 *      dbreg_new_id (Assigns new ID to dbp and logs it.  May be postponed
34 *      until we attempt to log something else using that dbp, if the dbp
35 *      was opened on a replication client.)
36 *
37 * CLOSE
38 *      dbreg_close_id  (Logs closure of dbp/revocation of ID.)
39 *      dbreg_revoke_id (As name implies, revokes ID.)
40 *      dbreg_teardown (Destroys FNAME.)
41 *
42 * RECOVERY
43 *      dbreg_setup
44 *      dbreg_assign_id (Assigns a particular ID we have in the log to a dbp.)
45 *
46 *      sometimes: dbreg_revoke_id; dbreg_teardown
47 *      other times: normal close path
48 *
49 * A note about locking:
50 *
51 *      FNAME structures are referenced only by their corresponding dbp's
52 *      until they have a valid id.
53 *
54 *      Once they have a valid id, they must get linked into the log
55 *      region list so they can get logged on checkpoints.
56 *
57 *      An FNAME that may/does have a valid id must be accessed under
58 *      protection of the fq_mutex, with the following exception:
59 *
60 *      We don't want to have to grab the fq_mutex on every log
61 *      record, and it should be safe not to do so when we're just
62 *      looking at the id, because once allocated, the id should
63 *      not change under a handle until the handle is closed.
64 *
65 *      If a handle is closed during an attempt by another thread to
66 *      log with it, well, the application doing the close deserves to
67 *      go down in flames and a lot else is about to fail anyway.
68 *
69 *      When in the course of logging we encounter an invalid id
70 *      and go to allocate it lazily, we *do* need to check again
71 *      after grabbing the mutex, because it's possible to race with
72 *      another thread that has also decided that it needs to allocate
73 *      a id lazily.
74 *
75 * See SR #5623 for further discussion of the new dbreg design.
76 */
77
78/*
79 * __dbreg_setup --
80 *      Allocate and initialize an FNAME structure.  The FNAME structures
81 * live in the log shared region and map one-to-one with open database handles.
82 * When the handle needs to be logged, the FNAME should have a valid fid
83 * allocated.  If the handle currently isn't logged, it still has an FNAME
84 * entry.  If we later discover that the handle needs to be logged, we can
85 * allocate a id for it later.  (This happens when the handle is on a
86 * replication client that later becomes a master.)
87 *
88 * PUBLIC: int __dbreg_setup __P((DB *, const char *, u_int32_t));
89 */
90int
91__dbreg_setup(dbp, name, create_txnid)
92        DB *dbp;
93        const char *name;
94        u_int32_t create_txnid;
95{
96        DB_ENV *dbenv;
97        DB_LOG *dblp;
98        FNAME *fnp;
99        int ret;
100        size_t len;
101        void *namep;
102
103        dbenv = dbp->dbenv;
104        dblp = dbenv->lg_handle;
105
106        fnp = NULL;
107        namep = NULL;
108
109        /* Allocate an FNAME and, if necessary, a buffer for the name itself. */
110        R_LOCK(dbenv, &dblp->reginfo);
111        if ((ret =
112            __db_shalloc(dblp->reginfo.addr, sizeof(FNAME), 0, &fnp)) != 0) {
113                R_UNLOCK(dbenv, &dblp->reginfo);
114                return (ret);
115        }
116        memset(fnp, 0, sizeof(FNAME));
117        if (name != NULL) {
118                len = strlen(name) + 1;
119                if ((ret = __db_shalloc(dblp->reginfo.addr,
120                    len, 0, &namep)) != 0) {
121                        R_UNLOCK(dbenv, &dblp->reginfo);
122                        return (ret);
123                }
124                fnp->name_off = R_OFFSET(&dblp->reginfo, namep);
125                memcpy(namep, name, len);
126        } else
127                fnp->name_off = INVALID_ROFF;
128
129        R_UNLOCK(dbenv, &dblp->reginfo);
130
131        /*
132         * Fill in all the remaining info that we'll need later to register
133         * the file, if we use it for logging.
134         */
135        fnp->id = DB_LOGFILEID_INVALID;
136        fnp->s_type = dbp->type;
137        memcpy(fnp->ufid, dbp->fileid, DB_FILE_ID_LEN);
138        fnp->meta_pgno = dbp->meta_pgno;
139        fnp->create_txnid = create_txnid;
140
141        dbp->log_filename = fnp;
142
143        return (0);
144}
145
146/*
147 * __dbreg_teardown --
148 *      Destroy a DB handle's FNAME struct.
149 *
150 * PUBLIC: int __dbreg_teardown __P((DB *));
151 */
152int
153__dbreg_teardown(dbp)
154        DB *dbp;
155{
156        DB_ENV *dbenv;
157        DB_LOG *dblp;
158        FNAME *fnp;
159
160        dbenv = dbp->dbenv;
161        dblp = dbenv->lg_handle;
162        fnp = dbp->log_filename;
163
164        /*
165         * We may not have an FNAME if we were never opened.  This is not an
166         * error.
167         */
168        if (fnp == NULL)
169                return (0);
170
171        DB_ASSERT(fnp->id == DB_LOGFILEID_INVALID);
172
173        R_LOCK(dbenv, &dblp->reginfo);
174        if (fnp->name_off != INVALID_ROFF)
175                __db_shalloc_free(dblp->reginfo.addr,
176                    R_ADDR(&dblp->reginfo, fnp->name_off));
177        __db_shalloc_free(dblp->reginfo.addr, fnp);
178        R_UNLOCK(dbenv, &dblp->reginfo);
179
180        dbp->log_filename = NULL;
181
182        return (0);
183}
184
185/*
186 * __dbreg_new_id --
187 *      Assign an unused dbreg id to this database handle.
188 *
189 * PUBLIC: int __dbreg_new_id __P((DB *, DB_TXN *));
190 */
191int
192__dbreg_new_id(dbp, txn)
193        DB *dbp;
194        DB_TXN *txn;
195{
196        DBT fid_dbt, r_name;
197        DB_ENV *dbenv;
198        DB_LOG *dblp;
199        DB_LSN unused;
200        FNAME *fnp;
201        LOG *lp;
202        int32_t id;
203        int ret;
204
205        dbenv = dbp->dbenv;
206        dblp = dbenv->lg_handle;
207        lp = dblp->reginfo.primary;
208        fnp = dbp->log_filename;
209
210        /* The fq_mutex protects the FNAME list and id management. */
211        MUTEX_LOCK(dbenv, &lp->fq_mutex);
212
213        /*
214         * It's possible that after deciding we needed to call this function,
215         * someone else allocated an ID before we grabbed the lock.  Check
216         * to make sure there was no race and we have something useful to do.
217         */
218        if (fnp->id != DB_LOGFILEID_INVALID) {
219                MUTEX_UNLOCK(dbenv, &lp->fq_mutex);
220                return (0);
221        }
222
223        /* Get an unused ID from the free list. */
224        if ((ret = __dbreg_pop_id(dbenv, &id)) != 0)
225                goto err;
226
227        /* If no ID was found, allocate a new one. */
228        if (id == DB_LOGFILEID_INVALID)
229                id = lp->fid_max++;
230
231        fnp->id = id;
232
233        /* Hook the FNAME into the list of open files. */
234        SH_TAILQ_INSERT_HEAD(&lp->fq, fnp, q, __fname);
235
236        /*
237         * Log the registry.  We should only request a new ID in situations
238         * where logging is reasonable.
239         */
240        DB_ASSERT(!F_ISSET(dbp, DB_AM_RECOVER));
241
242        memset(&fid_dbt, 0, sizeof(fid_dbt));
243        memset(&r_name, 0, sizeof(r_name));
244        if (fnp->name_off != INVALID_ROFF) {
245                r_name.data = R_ADDR(&dblp->reginfo, fnp->name_off);
246                r_name.size = (u_int32_t)strlen((char *)r_name.data) + 1;
247        }
248        fid_dbt.data = dbp->fileid;
249        fid_dbt.size = DB_FILE_ID_LEN;
250        if ((ret = __dbreg_register_log(dbenv, txn, &unused, 0, LOG_OPEN,
251            r_name.size == 0 ? NULL : &r_name, &fid_dbt, id, fnp->s_type,
252            fnp->meta_pgno, fnp->create_txnid)) != 0)
253                goto err;
254
255        DB_ASSERT(dbp->type == fnp->s_type);
256        DB_ASSERT(dbp->meta_pgno == fnp->meta_pgno);
257
258        if ((ret = __dbreg_add_dbentry(dbenv, dblp, dbp, id)) != 0)
259                goto err;
260
261err:    MUTEX_UNLOCK(dbenv, &lp->fq_mutex);
262        return (ret);
263}
264
265/*
266 * __dbreg_assign_id --
267 *      Assign a particular dbreg id to this database handle.
268 *
269 * PUBLIC: int __dbreg_assign_id __P((DB *, int32_t));
270 */
271int
272__dbreg_assign_id(dbp, id)
273        DB *dbp;
274        int32_t id;
275{
276        DB *close_dbp;
277        DB_ENV *dbenv;
278        DB_LOG *dblp;
279        FNAME *close_fnp, *fnp;
280        LOG *lp;
281        int ret;
282
283        dbenv = dbp->dbenv;
284        dblp = dbenv->lg_handle;
285        lp = dblp->reginfo.primary;
286        fnp = dbp->log_filename;
287
288        close_dbp = NULL;
289        close_fnp = NULL;
290
291        /* The fq_mutex protects the FNAME list and id management. */
292        MUTEX_LOCK(dbenv, &lp->fq_mutex);
293
294        /* We should only call this on DB handles that have no ID. */
295        DB_ASSERT(fnp->id == DB_LOGFILEID_INVALID);
296
297        /*
298         * Make sure there isn't already a file open with this ID. There can
299         * be in recovery, if we're recovering across a point where an ID got
300         * reused.
301         */
302        if (__dbreg_id_to_fname(dblp, id, 1, &close_fnp) == 0) {
303                /*
304                 * We want to save off any dbp we have open with this id.
305                 * We can't safely close it now, because we hold the fq_mutex,
306                 * but we should be able to rely on it being open in this
307                 * process, and we're running recovery, so no other thread
308                 * should muck with it if we just put off closing it until
309                 * we're ready to return.
310                 *
311                 * Once we have the dbp, revoke its id;  we're about to
312                 * reuse it.
313                 */
314                ret = __dbreg_id_to_db_int(dbenv, NULL, &close_dbp, id, 0, 0);
315                if (ret == ENOENT) {
316                        ret = 0;
317                        goto cont;
318                } else if (ret != 0)
319                        goto err;
320
321                if ((ret = __dbreg_revoke_id(close_dbp, 1)) != 0)
322                        goto err;
323        }
324
325        /*
326         * Remove this ID from the free list, if it's there, and make sure
327         * we don't allocate it anew.
328         */
329cont:   if ((ret = __dbreg_pluck_id(dbenv, id)) != 0)
330                goto err;
331        if (id >= lp->fid_max)
332                lp->fid_max = id + 1;
333
334        /* Now go ahead and assign the id to our dbp. */
335        fnp->id = id;
336        SH_TAILQ_INSERT_HEAD(&lp->fq, fnp, q, __fname);
337
338        if ((ret = __dbreg_add_dbentry(dbenv, dblp, dbp, id)) != 0)
339                goto err;
340
341err:    MUTEX_UNLOCK(dbenv, &lp->fq_mutex);
342
343        /* There's nothing useful that our caller can do if this close fails. */
344        if (close_dbp != NULL)
345                (void)close_dbp->close(close_dbp, DB_NOSYNC);
346
347        return (ret);
348}
349
350/*
351 * __dbreg_revoke_id --
352 *      Take a log id away from a dbp, in preparation for closing it,
353 *      but without logging the close.
354 *
355 * PUBLIC: int __dbreg_revoke_id __P((DB *, int));
356 */
357int
358__dbreg_revoke_id(dbp, have_lock)
359        DB *dbp;
360        int have_lock;
361{
362        DB_ENV *dbenv;
363        DB_LOG *dblp;
364        FNAME *fnp;
365        LOG *lp;
366        int32_t id;
367        int ret;
368
369        dbenv = dbp->dbenv;
370        dblp = dbenv->lg_handle;
371        lp = dblp->reginfo.primary;
372        fnp = dbp->log_filename;
373
374        /* If we lack an ID, this is a null-op. */
375        if (fnp == NULL || fnp->id == DB_LOGFILEID_INVALID)
376                return (0);
377
378        if (!have_lock)
379                MUTEX_LOCK(dbenv, &lp->fq_mutex);
380
381        id = fnp->id;
382        fnp->id = DB_LOGFILEID_INVALID;
383
384        /* Remove the FNAME from the list of open files. */
385        SH_TAILQ_REMOVE(&lp->fq, fnp, q, __fname);
386
387        /* Remove this id from the dbentry table. */
388        __dbreg_rem_dbentry(dblp, id);
389
390        /* Push this id onto the free list. */
391        ret = __dbreg_push_id(dbenv, id);
392
393        if (!have_lock)
394                MUTEX_UNLOCK(dbenv, &lp->fq_mutex);
395        return (ret);
396}
397
398/*
399 * __dbreg_close_id --
400 *      Take a dbreg id away from a dbp that we're closing, and log
401 * the unregistry.
402 *
403 * PUBLIC: int __dbreg_close_id __P((DB *, DB_TXN *));
404 */
405int
406__dbreg_close_id(dbp, txn)
407        DB *dbp;
408        DB_TXN *txn;
409{
410        DBT fid_dbt, r_name, *dbtp;
411        DB_ENV *dbenv;
412        DB_LOG *dblp;
413        DB_LSN r_unused;
414        FNAME *fnp;
415        LOG *lp;
416        int ret;
417
418        dbenv = dbp->dbenv;
419        dblp = dbenv->lg_handle;
420        lp = dblp->reginfo.primary;
421        fnp = dbp->log_filename;
422
423        /* If we lack an ID, this is a null-op. */
424        if (fnp == NULL || fnp->id == DB_LOGFILEID_INVALID)
425                return (0);
426
427        MUTEX_LOCK(dbenv, &lp->fq_mutex);
428
429        if (fnp->name_off == INVALID_ROFF)
430                dbtp = NULL;
431        else {
432                memset(&r_name, 0, sizeof(r_name));
433                r_name.data = R_ADDR(&dblp->reginfo, fnp->name_off);
434                r_name.size =
435                    (u_int32_t)strlen((char *)r_name.data) + 1;
436                dbtp = &r_name;
437        }
438        memset(&fid_dbt, 0, sizeof(fid_dbt));
439        fid_dbt.data = fnp->ufid;
440        fid_dbt.size = DB_FILE_ID_LEN;
441        if ((ret = __dbreg_register_log(dbenv, txn,
442            &r_unused, 0, LOG_CLOSE, dbtp, &fid_dbt, fnp->id,
443            fnp->s_type, fnp->meta_pgno, TXN_INVALID)) != 0)
444                goto err;
445
446        ret = __dbreg_revoke_id(dbp, 1);
447
448err:    MUTEX_UNLOCK(dbenv, &lp->fq_mutex);
449        return (ret);
450}
Note: See TracBrowser for help on using the repository browser.