first check-in of feisty meow codebase. many things broken still due to recent
[feisty_meow.git] / core / library / structures / memory_limiter.cpp
1 /*****************************************************************************\
2 *                                                                             *
3 *  Name   : memory_limiter                                                    *
4 *  Author : Chris Koeritz                                                     *
5 *                                                                             *
6 *******************************************************************************
7 * Copyright (c) 2001-$now By Author.  This program is free software; you can  *
8 * redistribute it and/or modify it under the terms of the GNU General Public  *
9 * License as published by the Free Software Foundation; either version 2 of   *
10 * the License or (at your option) any later version.  This is online at:      *
11 *     http://www.fsf.org/copyleft/gpl.html                                    *
12 * Please send any updates to: fred@gruntose.com                               *
13 \*****************************************************************************/
14
15 #include "int_hash.h"
16 #include "memory_limiter.h"
17
18 #include <basis/functions.h>
19
20 #include <stdio.h>
21
22 using namespace basis;
23
24 namespace structures {
25
26 #undef LOG
27 #define LOG(to_print) printf("%s\n", astring(to_print).s())
28
29 class ml_memory_record
30 {
31 public:
32   int _usage;
33
34   ml_memory_record(int initial) : _usage(initial) {}
35 };
36
37 //////////////
38
39 class ml_memory_state_meter : public int_hash<ml_memory_record>
40 {
41 public:
42   ml_memory_state_meter() : int_hash<ml_memory_record>(10) {}
43 };
44
45 //////////////
46
47 memory_limiter::memory_limiter(int overall_limit, int individual_limit)
48 : _overall_limit(overall_limit),
49   _individual_limit(individual_limit),
50   _overall_size(0),
51   _individual_sizes(new ml_memory_state_meter)
52 {
53 }
54
55 memory_limiter::~memory_limiter()
56 {
57   WHACK(_individual_sizes);
58 }
59
60 void memory_limiter::reset()
61 {
62   _overall_size = 0;
63   _individual_sizes->reset();
64 }
65
66 const int_set &memory_limiter::individuals_listed() const
67 { return _individual_sizes->ids(); }
68
69 ml_memory_record *memory_limiter::find_individual(int individual) const
70 {
71   ml_memory_record *to_return = NIL;
72   if (!_individual_sizes->find(individual, to_return)) return NIL;
73     // no record for that guy.
74   return to_return;
75 }
76
77 int memory_limiter::individual_usage(int individual) const
78 {
79   ml_memory_record *found = find_individual(individual);
80   if (!found) return 0;
81   return found->_usage;
82 }
83
84 int memory_limiter::individual_space_left(int individual) const
85 {
86   if (!individual_limit()) return 0;
87   return individual_limit() - individual_usage(individual);
88 }
89
90 astring memory_limiter::text_form(int indent) const
91 {
92   astring to_return;
93   astring indentat(' ', indent);
94
95   astring allowed = overall_limit()?
96       astring(astring::SPRINTF, "%dK", overall_limit() / KILOBYTE)
97       : "unlimited";
98   astring avail = overall_limit()?
99       astring(astring::SPRINTF, "%dK", overall_space_left() / KILOBYTE)
100       : "unlimited";
101
102   to_return += astring(astring::SPRINTF, "Overall Limit=%s, Allocations=%dK, "
103       "Free Space=%s", allowed.s(), overall_usage() / KILOBYTE, avail.s());
104   to_return += "\n";
105
106   int_set individuals = _individual_sizes->ids();
107   for (int i = 0; i < individuals.elements(); i++) {
108     astring allowed = individual_limit()?
109         astring(astring::SPRINTF, "%dK", individual_limit() / KILOBYTE)
110         : "unlimited";
111     astring avail = individual_limit()?
112         astring(astring::SPRINTF, "%dK",
113         individual_space_left(individuals[i]) / KILOBYTE) : "unlimited";
114
115     to_return += indentat + astring(astring::SPRINTF, "individual %d: "
116         "Limit=%s, Used=%dK, Free=%s", individuals[i], allowed.s(),
117         individual_usage(individuals[i]) / KILOBYTE, avail.s());
118     to_return += "\n";
119   }
120   if (!individuals.elements()) {
121     to_return += indentat + "No allocations owned currently.";
122     to_return += "\n";
123   }
124   return to_return;
125 }
126
127 bool memory_limiter::okay_allocation(int individual, int memory_desired)
128 {
129 //  FUNCDEF("okay_allocation");
130   // check the overall allocation limits first.
131   if (_overall_limit
132       && (_overall_size + memory_desired > _overall_limit) ) return false;
133   // now check sanity of this request.
134   if (_individual_limit && (memory_desired > _individual_limit) ) return false;
135   // now check the allocations per user.
136   ml_memory_record *found = find_individual(individual);
137   if (!found) {
138     _individual_sizes->add(individual, new ml_memory_record(0));
139     found = find_individual(individual);
140     if (!found) {
141       LOG("ERROR: adding a new record to the memory state!");
142       return false;
143     }
144   }
145   if (_individual_limit
146       && (found->_usage + memory_desired > _individual_limit) )
147     return false; 
148   found->_usage += memory_desired;
149   _overall_size += memory_desired;
150   return true;
151 }
152
153 bool memory_limiter::record_deletion(int individual, int memory_deleted)
154 {
155   if (memory_deleted < 0) return false;  // bogus.
156   // make sure the individual exists.
157   ml_memory_record *found = find_individual(individual);
158   if (!found) return false;
159   // the individual must have actually allocated at least that much previously.
160   if (found->_usage < memory_deleted) return false;
161   // okay, we think that's reasonable.
162   found->_usage -= memory_deleted;
163   _overall_size -= memory_deleted;
164   // clean out an empty locker.
165   if (!found->_usage) _individual_sizes->zap(individual);
166   return true;
167 }
168
169 } //namespace.
170
171