descriptors.cc revision 1.1.1.1
1// descriptors.cc -- manage file descriptors for gold
2
3// Copyright 2008 Free Software Foundation, Inc.
4// Written by Ian Lance Taylor <iant@google.com>.
5
6// This file is part of gold.
7
8// This program is free software; you can redistribute it and/or modify
9// it under the terms of the GNU General Public License as published by
10// the Free Software Foundation; either version 3 of the License, or
11// (at your option) any later version.
12
13// This program is distributed in the hope that it will be useful,
14// but WITHOUT ANY WARRANTY; without even the implied warranty of
15// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16// GNU General Public License for more details.
17
18// You should have received a copy of the GNU General Public License
19// along with this program; if not, write to the Free Software
20// Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
21// MA 02110-1301, USA.
22
23#include "gold.h"
24
25#include <cerrno>
26#include <cstring>
27#include <fcntl.h>
28#include <unistd.h>
29
30#include "parameters.h"
31#include "gold-threads.h"
32#include "descriptors.h"
33
34namespace gold
35{
36
37// Class Descriptors.
38
39// The default for limit_ is meant to simply be large.  It gets
40// adjusted downward if we run out of file descriptors.
41
42Descriptors::Descriptors()
43  : lock_(NULL), open_descriptors_(), stack_top_(-1), current_(0),
44    limit_(8192 - 16)
45{
46  this->open_descriptors_.reserve(128);
47}
48
49// Open a file.
50
51int
52Descriptors::open(int descriptor, const char* name, int flags, int mode)
53{
54  // We don't initialize this until we are called, because we can't
55  // initialize a Lock until we have parsed the options to find out
56  // whether we are running with threads.  We can be called before
57  // options are valid when reading a linker script.
58  if (this->lock_ == NULL)
59    {
60      if (parameters->options_valid())
61	this->lock_ = new Lock();
62      else
63	gold_assert(descriptor < 0);
64    }
65
66  if (descriptor >= 0)
67    {
68      Hold_lock hl(*this->lock_);
69
70      gold_assert(static_cast<size_t>(descriptor)
71		  < this->open_descriptors_.size());
72      Open_descriptor* pod = &this->open_descriptors_[descriptor];
73      if (pod->name == name
74	  || (pod->name != NULL && strcmp(pod->name, name) == 0))
75	{
76	  gold_assert(!pod->inuse);
77	  pod->inuse = true;
78	  return descriptor;
79	}
80    }
81
82  while (true)
83    {
84      int new_descriptor = ::open(name, flags, mode);
85      if (new_descriptor < 0
86	  && errno != ENFILE
87	  && errno != EMFILE)
88	{
89	  if (descriptor >= 0 && errno == ENOENT)
90	    {
91	      {
92		Hold_lock hl(*this->lock_);
93
94		gold_error(_("file %s was removed during the link"),
95			   this->open_descriptors_[descriptor].name);
96	      }
97
98	      errno = ENOENT;
99	    }
100
101	  return new_descriptor;
102	}
103
104      if (new_descriptor >= 0)
105	{
106	  Hold_optional_lock hl(this->lock_);
107
108	  if (static_cast<size_t>(new_descriptor)
109	      >= this->open_descriptors_.size())
110	    this->open_descriptors_.resize(new_descriptor + 64);
111
112	  Open_descriptor* pod = &this->open_descriptors_[new_descriptor];
113	  pod->name = name;
114	  pod->stack_next = -1;
115	  pod->inuse = true;
116	  pod->is_write = (flags & O_ACCMODE) != O_RDONLY;
117
118	  ++this->current_;
119	  if (this->current_ >= this->limit_)
120	    this->close_some_descriptor();
121
122	  return new_descriptor;
123	}
124
125      // We ran out of file descriptors.
126      {
127	Hold_optional_lock hl(this->lock_);
128
129	this->limit_ = this->current_ - 16;
130	if (this->limit_ < 8)
131	  this->limit_ = 8;
132	if (!this->close_some_descriptor())
133	  gold_fatal(_("out of file descriptors and couldn't close any"));
134      }
135    }
136}
137
138// Release a descriptor.
139
140void
141Descriptors::release(int descriptor, bool permanent)
142{
143  Hold_optional_lock hl(this->lock_);
144
145  gold_assert(descriptor >= 0
146	      && (static_cast<size_t>(descriptor)
147		  < this->open_descriptors_.size()));
148  Open_descriptor* pod = &this->open_descriptors_[descriptor];
149
150  if (permanent
151      || (this->current_ > this->limit_ && !pod->is_write))
152    {
153      if (::close(descriptor) < 0)
154	gold_warning(_("while closing %s: %s"), pod->name, strerror(errno));
155      pod->name = NULL;
156      --this->current_;
157    }
158  else
159    {
160      pod->inuse = false;
161      if (!pod->is_write)
162	{
163	  pod->stack_next = this->stack_top_;
164	  this->stack_top_ = descriptor;
165	}
166    }
167}
168
169// Close some descriptor.  The lock is held when this is called.  We
170// close the descriptor on the top of the free stack.  Note that this
171// is the opposite of an LRU algorithm--we close the most recently
172// used descriptor.  That is because the linker tends to cycle through
173// all the files; after we release a file, we are unlikely to need it
174// again until we have looked at all the other files.  Return true if
175// we closed a descriptor.
176
177bool
178Descriptors::close_some_descriptor()
179{
180  int last = -1;
181  int i = this->stack_top_;
182  while (i >= 0)
183    {
184      gold_assert(static_cast<size_t>(i) < this->open_descriptors_.size());
185      Open_descriptor* pod = &this->open_descriptors_[i];
186      if (!pod->inuse && !pod->is_write)
187	{
188	  if (::close(i) < 0)
189	    gold_warning(_("while closing %s: %s"), pod->name, strerror(errno));
190	  --this->current_;
191	  pod->name = NULL;
192	  if (last < 0)
193	    this->stack_top_ = pod->stack_next;
194	  else
195	    this->open_descriptors_[last].stack_next = pod->stack_next;
196	  return true;
197	}
198      last = i;
199      i = pod->stack_next;
200    }
201
202  // We couldn't find any descriptors to close.  This is weird but not
203  // necessarily an error.
204  return false;
205}
206
207// The single global variable which manages descriptors.
208
209Descriptors descriptors;
210
211} // End namespace gold.
212