4343 * wrappers around fopen(3), opendir(3), popen(3) and open(2), respectively.
4444 * They behave like the corresponding native functions, except that the handle
4545 * is registered with the current subtransaction, and will be automatically
46- * closed at abort. These are intended for short operations like reading a
47- * configuration file, and there is a fixed limit on the number of files that
46+ * closed at abort. These are intended mainly for short operations like
47+ * reading a configuration file; there is a limit on the number of files that
4848 * can be opened using these functions at any one time.
4949 *
5050 * Finally, BasicOpenFile is just a thin wrapper around open() that can
@@ -198,13 +198,7 @@ static uint64 temporary_files_size = 0;
198198/*
199199 * List of OS handles opened with AllocateFile, AllocateDir and
200200 * OpenTransientFile.
201- *
202- * Since we don't want to encourage heavy use of those functions,
203- * it seems OK to put a pretty small maximum limit on the number of
204- * simultaneously allocated descs.
205201 */
206- #define MAX_ALLOCATED_DESCS 32
207-
208202typedef enum
209203{
210204 AllocateDescFile ,
@@ -216,17 +210,18 @@ typedef enum
216210typedef struct
217211{
218212 AllocateDescKind kind ;
213+ SubTransactionId create_subid ;
219214 union
220215 {
221216 FILE * file ;
222217 DIR * dir ;
223218 int fd ;
224219 } desc ;
225- SubTransactionId create_subid ;
226220} AllocateDesc ;
227221
228222static int numAllocatedDescs = 0 ;
229- static AllocateDesc allocatedDescs [MAX_ALLOCATED_DESCS ];
223+ static int maxAllocatedDescs = 0 ;
224+ static AllocateDesc * allocatedDescs = NULL ;
230225
231226/*
232227 * Number of temporary files opened during the current session;
@@ -252,6 +247,7 @@ static int nextTempTableSpace = 0;
252247 * Insert - put a file at the front of the Lru ring
253248 * LruInsert - put a file at the front of the Lru ring and open it
254249 * ReleaseLruFile - Release an fd by closing the last entry in the Lru ring
250+ * ReleaseLruFiles - Release fd(s) until we're under the max_safe_fds limit
255251 * AllocateVfd - grab a free (or new) file record (from VfdArray)
256252 * FreeVfd - free a file record
257253 *
@@ -279,11 +275,14 @@ static void LruDelete(File file);
279275static void Insert (File file );
280276static int LruInsert (File file );
281277static bool ReleaseLruFile (void );
278+ static void ReleaseLruFiles (void );
282279static File AllocateVfd (void );
283280static void FreeVfd (File file );
284281
285282static int FileAccess (File file );
286283static File OpenTemporaryFileInTablespace (Oid tblspcOid , bool rejectError );
284+ static bool reserveAllocatedDesc (void );
285+ static int FreeDesc (AllocateDesc * desc );
287286static void AtProcExit_Files (int code , Datum arg );
288287static void CleanupTempFiles (bool isProcExit );
289288static void RemovePgTempFilesInDir (const char * tmpdirname );
@@ -693,11 +692,8 @@ LruInsert(File file)
693692
694693 if (FileIsNotOpen (file ))
695694 {
696- while (nfile + numAllocatedDescs >= max_safe_fds )
697- {
698- if (!ReleaseLruFile ())
699- break ;
700- }
695+ /* Close excess kernel FDs. */
696+ ReleaseLruFiles ();
701697
702698 /*
703699 * The open could still fail for lack of file descriptors, eg due to
@@ -736,6 +732,9 @@ LruInsert(File file)
736732 return 0 ;
737733}
738734
735+ /*
736+ * Release one kernel FD by closing the least-recently-used VFD.
737+ */
739738static bool
740739ReleaseLruFile (void )
741740{
@@ -754,6 +753,20 @@ ReleaseLruFile(void)
754753 return false; /* no files available to free */
755754}
756755
756+ /*
757+ * Release kernel FDs as needed to get under the max_safe_fds limit.
758+ * After calling this, it's OK to try to open another file.
759+ */
760+ static void
761+ ReleaseLruFiles (void )
762+ {
763+ while (nfile + numAllocatedDescs >= max_safe_fds )
764+ {
765+ if (!ReleaseLruFile ())
766+ break ;
767+ }
768+ }
769+
757770static File
758771AllocateVfd (void )
759772{
@@ -907,11 +920,8 @@ PathNameOpenFile(FileName fileName, int fileFlags, int fileMode)
907920 file = AllocateVfd ();
908921 vfdP = & VfdCache [file ];
909922
910- while (nfile + numAllocatedDescs >= max_safe_fds )
911- {
912- if (!ReleaseLruFile ())
913- break ;
914- }
923+ /* Close excess kernel FDs. */
924+ ReleaseLruFiles ();
915925
916926 vfdP -> fd = BasicOpenFile (fileName , fileFlags , fileMode );
917927
@@ -1490,6 +1500,66 @@ FilePathName(File file)
14901500}
14911501
14921502
1503+ /*
1504+ * Make room for another allocatedDescs[] array entry if needed and possible.
1505+ * Returns true if an array element is available.
1506+ */
1507+ static bool
1508+ reserveAllocatedDesc (void )
1509+ {
1510+ AllocateDesc * newDescs ;
1511+ int newMax ;
1512+
1513+ /* Quick out if array already has a free slot. */
1514+ if (numAllocatedDescs < maxAllocatedDescs )
1515+ return true;
1516+
1517+ /*
1518+ * If the array hasn't yet been created in the current process, initialize
1519+ * it with FD_MINFREE / 2 elements. In many scenarios this is as many as
1520+ * we will ever need, anyway. We don't want to look at max_safe_fds
1521+ * immediately because set_max_safe_fds() may not have run yet.
1522+ */
1523+ if (allocatedDescs == NULL )
1524+ {
1525+ newMax = FD_MINFREE / 2 ;
1526+ newDescs = (AllocateDesc * ) malloc (newMax * sizeof (AllocateDesc ));
1527+ /* Out of memory already? Treat as fatal error. */
1528+ if (newDescs == NULL )
1529+ ereport (ERROR ,
1530+ (errcode (ERRCODE_OUT_OF_MEMORY ),
1531+ errmsg ("out of memory" )));
1532+ allocatedDescs = newDescs ;
1533+ maxAllocatedDescs = newMax ;
1534+ return true;
1535+ }
1536+
1537+ /*
1538+ * Consider enlarging the array beyond the initial allocation used above.
1539+ * By the time this happens, max_safe_fds should be known accurately.
1540+ *
1541+ * We mustn't let allocated descriptors hog all the available FDs, and in
1542+ * practice we'd better leave a reasonable number of FDs for VFD use. So
1543+ * set the maximum to max_safe_fds / 2. (This should certainly be at
1544+ * least as large as the initial size, FD_MINFREE / 2.)
1545+ */
1546+ newMax = max_safe_fds / 2 ;
1547+ if (newMax > maxAllocatedDescs )
1548+ {
1549+ newDescs = (AllocateDesc * ) realloc (allocatedDescs ,
1550+ newMax * sizeof (AllocateDesc ));
1551+ /* Treat out-of-memory as a non-fatal error. */
1552+ if (newDescs == NULL )
1553+ return false;
1554+ allocatedDescs = newDescs ;
1555+ maxAllocatedDescs = newMax ;
1556+ return true;
1557+ }
1558+
1559+ /* Can't enlarge allocatedDescs[] any more. */
1560+ return false;
1561+ }
1562+
14931563/*
14941564 * Routines that want to use stdio (ie, FILE*) should use AllocateFile
14951565 * rather than plain fopen(). This lets fd.c deal with freeing FDs if
@@ -1515,16 +1585,15 @@ AllocateFile(const char *name, const char *mode)
15151585 DO_DB (elog (LOG , "AllocateFile: Allocated %d (%s)" ,
15161586 numAllocatedDescs , name ));
15171587
1518- /*
1519- * The test against MAX_ALLOCATED_DESCS prevents us from overflowing
1520- * allocatedFiles[]; the test against max_safe_fds prevents AllocateFile
1521- * from hogging every one of the available FDs, which'd lead to infinite
1522- * looping.
1523- */
1524- if (numAllocatedDescs >= MAX_ALLOCATED_DESCS ||
1525- numAllocatedDescs >= max_safe_fds - 1 )
1526- elog (ERROR , "exceeded MAX_ALLOCATED_DESCS while trying to open file \"%s\"" ,
1527- name );
1588+ /* Can we allocate another non-virtual FD? */
1589+ if (!reserveAllocatedDesc ())
1590+ ereport (ERROR ,
1591+ (errcode (ERRCODE_INSUFFICIENT_RESOURCES ),
1592+ errmsg ("exceeded maxAllocatedDescs (%d) while trying to open file \"%s\"" ,
1593+ maxAllocatedDescs , name )));
1594+
1595+ /* Close excess kernel FDs. */
1596+ ReleaseLruFiles ();
15281597
15291598TryAgain :
15301599 if ((file = fopen (name , mode )) != NULL )
@@ -1566,16 +1635,15 @@ OpenTransientFile(FileName fileName, int fileFlags, int fileMode)
15661635 DO_DB (elog (LOG , "OpenTransientFile: Allocated %d (%s)" ,
15671636 numAllocatedDescs , fileName ));
15681637
1569- /*
1570- * The test against MAX_ALLOCATED_DESCS prevents us from overflowing
1571- * allocatedFiles[]; the test against max_safe_fds prevents BasicOpenFile
1572- * from hogging every one of the available FDs, which'd lead to infinite
1573- * looping.
1574- */
1575- if (numAllocatedDescs >= MAX_ALLOCATED_DESCS ||
1576- numAllocatedDescs >= max_safe_fds - 1 )
1577- elog (ERROR , "exceeded MAX_ALLOCATED_DESCS while trying to open file \"%s\"" ,
1578- fileName );
1638+ /* Can we allocate another non-virtual FD? */
1639+ if (!reserveAllocatedDesc ())
1640+ ereport (ERROR ,
1641+ (errcode (ERRCODE_INSUFFICIENT_RESOURCES ),
1642+ errmsg ("exceeded maxAllocatedDescs (%d) while trying to open file \"%s\"" ,
1643+ maxAllocatedDescs , fileName )));
1644+
1645+ /* Close excess kernel FDs. */
1646+ ReleaseLruFiles ();
15791647
15801648 fd = BasicOpenFile (fileName , fileFlags , fileMode );
15811649
@@ -1607,16 +1675,15 @@ OpenPipeStream(const char *command, const char *mode)
16071675 DO_DB (elog (LOG , "OpenPipeStream: Allocated %d (%s)" ,
16081676 numAllocatedDescs , command ));
16091677
1610- /*
1611- * The test against MAX_ALLOCATED_DESCS prevents us from overflowing
1612- * allocatedFiles[]; the test against max_safe_fds prevents AllocateFile
1613- * from hogging every one of the available FDs, which'd lead to infinite
1614- * looping.
1615- */
1616- if (numAllocatedDescs >= MAX_ALLOCATED_DESCS ||
1617- numAllocatedDescs >= max_safe_fds - 1 )
1618- elog (ERROR , "exceeded MAX_ALLOCATED_DESCS while trying to execute command \"%s\"" ,
1619- command );
1678+ /* Can we allocate another non-virtual FD? */
1679+ if (!reserveAllocatedDesc ())
1680+ ereport (ERROR ,
1681+ (errcode (ERRCODE_INSUFFICIENT_RESOURCES ),
1682+ errmsg ("exceeded maxAllocatedDescs (%d) while trying to execute command \"%s\"" ,
1683+ maxAllocatedDescs , command )));
1684+
1685+ /* Close excess kernel FDs. */
1686+ ReleaseLruFiles ();
16201687
16211688TryAgain :
16221689 fflush (stdout );
@@ -1759,16 +1826,15 @@ AllocateDir(const char *dirname)
17591826 DO_DB (elog (LOG , "AllocateDir: Allocated %d (%s)" ,
17601827 numAllocatedDescs , dirname ));
17611828
1762- /*
1763- * The test against MAX_ALLOCATED_DESCS prevents us from overflowing
1764- * allocatedDescs[]; the test against max_safe_fds prevents AllocateDir
1765- * from hogging every one of the available FDs, which'd lead to infinite
1766- * looping.
1767- */
1768- if (numAllocatedDescs >= MAX_ALLOCATED_DESCS ||
1769- numAllocatedDescs >= max_safe_fds - 1 )
1770- elog (ERROR , "exceeded MAX_ALLOCATED_DESCS while trying to open directory \"%s\"" ,
1771- dirname );
1829+ /* Can we allocate another non-virtual FD? */
1830+ if (!reserveAllocatedDesc ())
1831+ ereport (ERROR ,
1832+ (errcode (ERRCODE_INSUFFICIENT_RESOURCES ),
1833+ errmsg ("exceeded maxAllocatedDescs (%d) while trying to open directory \"%s\"" ,
1834+ maxAllocatedDescs , dirname )));
1835+
1836+ /* Close excess kernel FDs. */
1837+ ReleaseLruFiles ();
17721838
17731839TryAgain :
17741840 if ((dir = opendir (dirname )) != NULL )
0 commit comments