diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index 2d1ed143e0..86a6df131b 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -44,6 +44,7 @@ #include "storage/procsignal.h" #include "storage/sinvaladt.h" #include "storage/spin.h" +#include "tsearch/ts_shared.h" #include "utils/backend_random.h" #include "utils/snapmgr.h" @@ -150,6 +151,7 @@ CreateSharedMemoryAndSemaphores(bool makePrivate, int port) size = add_size(size, SyncScanShmemSize()); size = add_size(size, AsyncShmemSize()); size = add_size(size, BackendRandomShmemSize()); + size = add_size(size, TsearchShmemSize()); #ifdef EXEC_BACKEND size = add_size(size, ShmemBackendArraySize()); #endif @@ -271,6 +273,11 @@ CreateSharedMemoryAndSemaphores(bool makePrivate, int port) AsyncShmemInit(); BackendRandomShmemInit(); + /* + * Set up shared memory to tsearch + */ + TsearchShmemInit(); + #ifdef EXEC_BACKEND /* diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c index eab98b0760..d8c8cc8cc3 100644 --- a/src/backend/storage/lmgr/lwlock.c +++ b/src/backend/storage/lmgr/lwlock.c @@ -520,6 +520,7 @@ RegisterLWLockTranches(void) "shared_tuplestore"); LWLockRegisterTranche(LWTRANCHE_TBM, "tbm"); LWLockRegisterTranche(LWTRANCHE_PARALLEL_APPEND, "parallel_append"); + LWLockRegisterTranche(LWTRANCHE_TSEARCH_DSA, "tsearch_dsa"); /* Register named tranches. */ for (i = 0; i < NamedLWLockTrancheRequests; i++) diff --git a/src/backend/tsearch/Makefile b/src/backend/tsearch/Makefile index 34fe4c5b3c..1c8c9c5ed7 100644 --- a/src/backend/tsearch/Makefile +++ b/src/backend/tsearch/Makefile @@ -26,7 +26,7 @@ DICTFILES_PATH=$(addprefix dicts/,$(DICTFILES)) OBJS = ts_locale.o ts_parse.o wparser.o wparser_def.o dict.o \ dict_simple.o dict_synonym.o dict_thesaurus.o \ dict_ispell.o regis.o spell.o \ - to_tsany.o ts_selfuncs.o ts_typanalyze.o ts_utils.o + to_tsany.o ts_selfuncs.o ts_shared.o ts_typanalyze.o ts_utils.o include $(top_srcdir)/src/backend/common.mk diff --git a/src/backend/tsearch/ts_shared.c b/src/backend/tsearch/ts_shared.c new file mode 100644 index 0000000000..03fe615b1c --- /dev/null +++ b/src/backend/tsearch/ts_shared.c @@ -0,0 +1,163 @@ +/*------------------------------------------------------------------------- + * + * ts_shared.c + * tsearch shared memory management + * + * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * IDENTIFICATION + * src/backend/tsearch/ts_shared.c + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "storage/dsm.h" +#include "storage/lwlock.h" +#include "storage/shmem.h" +#include "tsearch/ts_shared.h" + +/* XXX should it be a GUC-variable? */ +#define NUM_DICTIONARIES 20 + +typedef struct +{ + char dictfile[MAXPGPATH]; + char afffile[MAXPGPATH]; +} TsearchDictKey; + +typedef struct +{ + TsearchDictKey key; + dsm_handle dict_dsm; +} TsearchDictEntry; + +typedef struct +{ + LWLock lock; +} TsearchCtlData; + +static TsearchCtlData *tsearch_ctl; +static HTAB *dict_table; + +/* + * Return handle to a dynamic shared memory. + * + * dictbuild: building structure for the dictionary. + * dictfile: .dict file of the dictionary. + * afffile: .aff file of the dictionary. + * allocate_cb: function to build the dictionary, if it wasn't found in DSM. + */ +void * +ispell_shmem_location(void *dictbuild, + const char *dictfile, const char *afffile, + ispell_build_callback allocate_cb) +{ + TsearchDictKey key; + TsearchDictEntry *entry; + bool found; + dsm_segment *seg; + void *res; + + StrNCpy(key.dictfile, dictfile, MAXPGPATH); + StrNCpy(key.afffile, afffile, MAXPGPATH); + +refind_entry: + LWLockAcquire(&tsearch_ctl->lock, LW_SHARED); + + entry = (TsearchDictEntry *) hash_search(dict_table, &key, HASH_FIND, + &found); + + /* Dictionary wasn't load into memory */ + if (!found) + { + void *ispell_dict; + Size ispell_size; + + /* Try to get exclusive lock */ + LWLockRelease(&tsearch_ctl->lock); + if (!LWLockAcquireOrWait(&tsearch_ctl->lock, LW_EXCLUSIVE)) + { + /* + * The lock was released by another backend, try to refind an entry. + */ + goto refind_entry; + } + + entry = (TsearchDictEntry *) hash_search(dict_table, &key, HASH_ENTER, + &found); + + Assert(!found); + + /* The lock was free so add new entry */ + ispell_dict = allocate_cb(dictbuild, dictfile, afffile, &ispell_size); + + seg = dsm_create(ispell_size, 0); + res = dsm_segment_address(seg); + memcpy(res, ispell_dict, ispell_size); + + pfree(ispell_dict); + + entry->dict_dsm = dsm_segment_handle(seg); + + /* Remain attached until end of postmaster */ + dsm_pin_segment(seg); + + dsm_detach(seg); + } + else + { + seg = dsm_attach(entry->dict_dsm); + res = dsm_segment_address(seg); + + dsm_detach(seg); + } + + LWLockRelease(&tsearch_ctl->lock); + + return res; +} + +/* + * Allocate and initialize tsearch-related shared memory. + */ +void +TsearchShmemInit(void) +{ + HASHCTL ctl; + bool found; + + tsearch_ctl = (TsearchCtlData *) + ShmemInitStruct("Full Text Search Ctl", TsearchShmemSize(), &found); + + if (!found) + LWLockInitialize(&tsearch_ctl->lock, LWTRANCHE_TSEARCH_DSA); + + memset(&ctl, 0, sizeof(ctl)); + ctl.keysize = sizeof(TsearchDictKey); + ctl.entrysize = sizeof(TsearchDictEntry); + + dict_table = ShmemInitHash("Shared Tsearch Lookup Table", + NUM_DICTIONARIES, NUM_DICTIONARIES, + &ctl, + HASH_ELEM | HASH_BLOBS); +} + +/* + * Report shared memory space needed by TsearchShmemInit. + */ +Size +TsearchShmemSize(void) +{ + Size size = 0; + + /* size of service structure */ + size = add_size(size, MAXALIGN(sizeof(TsearchCtlData))); + + /* size of lookup hash table */ + size = add_size(size, hash_estimate_size(NUM_DICTIONARIES, + sizeof(TsearchDictEntry))); + + return size; +} diff --git a/src/include/storage/lwlock.h b/src/include/storage/lwlock.h index 97e4a0bbbd..3d41073b60 100644 --- a/src/include/storage/lwlock.h +++ b/src/include/storage/lwlock.h @@ -219,6 +219,7 @@ typedef enum BuiltinTrancheIds LWTRANCHE_SHARED_TUPLESTORE, LWTRANCHE_TBM, LWTRANCHE_PARALLEL_APPEND, + LWTRANCHE_TSEARCH_DSA, LWTRANCHE_FIRST_USER_DEFINED } BuiltinTrancheIds; diff --git a/src/include/tsearch/ts_shared.h b/src/include/tsearch/ts_shared.h new file mode 100644 index 0000000000..ded3a7c2ec --- /dev/null +++ b/src/include/tsearch/ts_shared.h @@ -0,0 +1,30 @@ +/*------------------------------------------------------------------------- + * + * ts_shared.h + * tsearch shared memory management + * + * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/tsearch/ts_shared.h + * + *------------------------------------------------------------------------- + */ +#ifndef TS_SHARED_H +#define TS_SHARED_H + +#include "c.h" + +typedef void *(*ispell_build_callback) (void *dictbuild, + const char *dictfile, + const char *afffile, + Size *size); + +extern void *ispell_shmem_location(void *dictbuild, + const char *dictfile, const char *afffile, + ispell_build_callback allocate_cb); + +extern void TsearchShmemInit(void); +extern Size TsearchShmemSize(void); + +#endif /* TS_SHARED_H */