kwave  18.07.70
MemoryManager.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  MemoryManager.cpp - Manager for virtual and physical memory
3  -------------------
4  begin : Wed Aug 08 2001
5  copyright : (C) 2000 by Thomas Eschenbacher
6  email : Thomas.Eschenbacher@gmx.de
7  ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 
18 #include "config.h"
19 
20 #include <limits.h>
21 #include <stdlib.h>
22 #include <sys/types.h>
23 #include <unistd.h>
24 
25 #include <QFile>
26 #include <QFileInfo>
27 #include <QLatin1Char>
28 #include <QMutexLocker>
29 #include <QString>
30 
31 #ifdef HAVE_SYSINFO
32 #include <linux/kernel.h> // for struct sysinfo
33 #include <sys/sysinfo.h> // for sysinfo()
34 #endif
35 
36 #ifdef HAVE_GETRLIMIT
37 #include <sys/resource.h> // for getrlimit()
38 #endif
39 
40 #include "libkwave/MemoryManager.h"
41 #include "libkwave/String.h"
42 #include "libkwave/SwapFile.h"
43 #include "libkwave/Utils.h"
44 #include "libkwave/memcpy.h"
45 
47 #define CACHE_SIZE 16
48 
51 
54 
55 //***************************************************************************
57  :m_physical_limit(0), m_physical_max(0), m_virtual_limit(0),
58  m_swap_dir(_("/tmp")), m_undo_limit(0),
59  m_physical(), m_unmapped_swap(), m_mapped_swap(),
60  m_cached_swap(), m_lock()
61 {
62  // reset statistics
63 #ifdef DEBUG_MEMORY
64  memset(&m_stats, 0x00, sizeof(m_stats));
65 #endif /* DEBUG_MEMORY */
66 
67  // determine amount of physical memory
68  // start with 1/4 of the theoretical address space
69 
70  // if sizeof(void *) == 4 -> 32 bit -> 1024 MB
71  // if sizeof(void *) == 8 -> 64 bit -> 4.398E12 MB
72  quint64 total = (1ULL << ((sizeof(void *) * 8ULL) - 22ULL));
73 
74  // limit the total memory to a 32bit value [MB]
75  if (total > (1ULL << 32)) total = (1ULL << 32) - 1;
76 #ifdef DEBUG
77  qDebug("Kwave::MemoryManager: theoretical limit: %llu MB", total);
78 #endif /* DEBUG */
79 
80 #ifdef HAVE_SYSINFO
81  // get the physically installed memory
82  quint64 installed_physical;
83  struct sysinfo info;
84 
85  sysinfo(&info);
86 
87  // find out installed memory and convert to megabytes
88 #ifdef HAVE_SYSINFO_MEMUNIT
89  installed_physical = (info.totalram * info.mem_unit) >> 20;
90 #ifdef DEBUG
91  qDebug("Kwave::MemoryManager: sysinfo/memunit: %llu MB", installed_physical);
92 #endif /* DEBUG */
93 #else /* HAVE_SYSINFO_MEMUNIT */
94  installed_physical = info.totalram >> 20;
95  qDebug("Kwave::MemoryManager: sysinfo: %llu MB", installed_physical);
96 #endif /* HAVE_SYSINFO_MEMUNIT */
97  if (installed_physical && (installed_physical < total))
98  total = installed_physical;
99 #endif /* HAVE_SYSINFO */
100 
101 #ifdef HAVE_GETRLIMIT
102  struct rlimit limit;
103 
104  // check ulimit of data segment size
105  if (getrlimit(RLIMIT_DATA, &limit) == 0) {
106  quint64 physical_ulimit =
107  qMin(limit.rlim_cur, limit.rlim_max) >> 20;
108 #ifdef DEBUG
109  qDebug("Kwave::MemoryManager: RLIMIT_DATA: %llu MB", physical_ulimit);
110 #endif /* DEBUG */
111  if (physical_ulimit < total) total = physical_ulimit;
112  }
113 
114  // check ulimit of total (virtual) system memory (address space)
115 #ifdef RLIMIT_AS
116  if (getrlimit(RLIMIT_AS, &limit) == 0) {
117  quint64 total_ulimit =
118  qMin(limit.rlim_cur, limit.rlim_max) >> 20;
119 #ifdef DEBUG
120  qDebug("Kwave::MemoryManager: RLIMIT_AS: %llu MB", total_ulimit);
121 #endif /* DEBUG */
122  if (total_ulimit < total) total = total_ulimit;
123  }
124 #endif /* RLIMIT_AS */
125 #endif /* HAVE_GETRLIMIT */
126 
127  // limit the total memory to a int value [MB]
128  // (values go into the GUI)
129  total = qMin(total, static_cast<quint64>(INT_MAX));
130 
131 #ifdef DEBUG
132  qDebug("Kwave::MemoryManager: => using up to %llu MB RAM", total);
133 #endif /* DEBUG */
134 
135  m_physical_max = total;
136  m_physical_limit = total;
137 
138 #ifdef DEBUG_MEMORY
139  m_stats.physical.limit = m_physical_limit << 20ULL;
140  m_stats.swap.limit = m_virtual_limit << 20ULL;
141 #endif /* DEBUG_MEMORY */
142 }
143 
144 //***************************************************************************
146 {
147  close();
148 }
149 
150 //***************************************************************************
152 {
153  QMutexLocker lock(&m_lock);
154 
155  // print warnings for each physical memory block
156  Q_ASSERT(m_physical.isEmpty());
157 
158  // remove all remaining swap files and print warnings
159  Q_ASSERT(m_unmapped_swap.isEmpty());
160  Q_ASSERT(m_mapped_swap.isEmpty());
161  Q_ASSERT(m_cached_swap.isEmpty());
162 }
163 
164 //***************************************************************************
166 {
167  return g_instance;
168 }
169 
170 //***************************************************************************
172 {
173  QMutexLocker lock(&m_lock);
174 
175  m_physical_limit = mb;
176  mb = totalPhysical();
177  if (m_physical_limit > mb) m_physical_limit = mb;
178 #ifdef DEBUG_MEMORY
179  m_stats.physical.limit = m_physical_limit << 20ULL;
180 #endif /* DEBUG_MEMORY */
181 }
182 
183 //***************************************************************************
185 {
186  QMutexLocker lock(&m_lock);
187 
188  m_virtual_limit = mb;
189 #ifdef DEBUG_MEMORY
190  m_stats.swap.limit = m_virtual_limit << 20ULL;
191 #endif /* DEBUG_MEMORY */
192 
194 // mb = totalVirtual();
195 // if (m_virtual_limit > mb) m_virtual_limit = mb;
196 }
197 
198 //***************************************************************************
200 {
201  QMutexLocker lock(&m_lock);
202  m_swap_dir = dir;
203 }
204 
205 //***************************************************************************
207 {
208  QMutexLocker lock(&m_lock);
209 
210  m_undo_limit = mb;
211  mb = totalPhysical();
212  if (m_undo_limit > mb) m_undo_limit = mb;
213 }
214 
215 //***************************************************************************
217 {
218  return m_undo_limit;
219 }
220 
221 //***************************************************************************
223 {
224  return m_physical_max;
225 }
226 
227 //***************************************************************************
229 {
230  for (unsigned int i = 0; i < INT_MAX; i++) {
231  // increment to get the next handle
232  m_last_handle++;
233 
234  // allow only non-zero positive values (handle wraparound)
235  if (m_last_handle <= 0) {
236  m_last_handle = 0;
237  continue;
238  }
239 
240  // if handle is in use -> next one please...
241  if (m_physical.contains(m_last_handle)) continue;
242  if (m_mapped_swap.contains(m_last_handle)) continue;
243  if (m_unmapped_swap.contains(m_last_handle)) continue;
244  if (m_cached_swap.contains(m_last_handle)) continue;
245 
246  // valid number and not in use -> found a new one :-)
247  return m_last_handle;
248  }
249 
250  // if we reach this point all handles are in use :-(
251  return 0;
252 }
253 
254 //***************************************************************************
256 {
257  size_t freed = 0;
258 
259  if (m_physical.isEmpty()) return false;
260 
261  QList<Kwave::Handle> handles = m_physical.keys();
262  QMutableListIterator<Kwave::Handle> it(handles);
263  it.toBack();
264  while (it.hasPrevious()) {
265  Kwave::Handle handle = it.previous();
266  const physical_memory_t &p = m_physical[handle];
267  if (p.m_mapcount) continue; // in use :-(
268 
269  // convert to swapfile
270  size_t s = p.m_size;
271 #if 0
272  qDebug("Kwave::MemoryManager[%9d] - swapping %2u MB out to make "\
273  "space for %2u MB", handle,
274  Kwave::toUint(s >> 20),
275  Kwave::toUint(size >> 20));
276 #endif
277 
278  if (convertToVirtual(handle, s)) {
279  freed += s;
280  if (freed >= size) return true;
281 
282  // abort if the list is now empty
283  if (m_physical.isEmpty()) return false;
284  }
285  }
286 
287  return false;
288 }
289 
290 //***************************************************************************
292 {
293  QMutexLocker lock(&m_lock);
294 
295  Kwave::Handle handle = allocatePhysical(size);
296  if (!handle) {
297  // try to make some room in the physical memory area
298  if (freePhysical(size)) {
299  // and try again to allocate physical memory
300  handle = allocatePhysical(size);
301  }
302 
303  if (!handle) {
304  // fallback: allocate a swapfile
305  handle = allocateVirtual(size);
306  }
307  }
308 
309 #ifdef DEBUG_MEMORY
310  if (!handle) {
311  qWarning("Kwave::MemoryManager::allocate(%u) - out of memory!",
312  Kwave::toUint(size));
313  }
314  dump("allocate");
315 #endif /* DEBUG_MEMORY */
316 
317  return handle;
318 }
319 
320 //***************************************************************************
322 {
323  // check for limits
324  quint64 limit = totalPhysical();
325  if (m_physical_limit < limit) limit = m_physical_limit;
326  quint64 used = physicalUsed();
327  quint64 available = (used < limit) ? (limit - used) : 0;
328  if ((size >> 20) >= available) return 0;
329 
330  // get a new handle
331  Kwave::Handle handle = newHandle();
332  if (!handle) return 0; // out of handles :-(
333 
334  // try to allocate via malloc
335  void *mem = ::malloc(size);
336  if (!mem) return 0; // out of memory :-(
337 
338  // store the object in the list of physical objects
339  physical_memory_t phys;
340  phys.m_data = mem;
341  phys.m_size = size;
342  phys.m_mapcount = 0;
343  Q_ASSERT(!m_physical.contains(handle));
344  m_physical.insert(handle, phys);
345 #ifdef DEBUG_MEMORY
346  m_stats.physical.handles++;
347  m_stats.physical.allocs++;
348  m_stats.physical.bytes += size;
349 #endif /* DEBUG_MEMORY */
350 
351  return handle;
352 }
353 
354 //***************************************************************************
356 {
357  quint64 used = 0;
358  foreach (const physical_memory_t &mem, m_physical.values())
359  used += (mem.m_size >> 10) + 1;
360  return (used >> 10);
361 }
362 
363 //***************************************************************************
365 {
366  quint64 used = 0;
367 
368  foreach (const Kwave::SwapFile *swapfile, m_cached_swap.values())
369  used += (swapfile->size() >> 10) + 1;
370 
371  foreach (const Kwave::SwapFile *swapfile, m_mapped_swap.values())
372  used += (swapfile->size() >> 10) + 1;
373 
374  foreach (const Kwave::SwapFile *swapfile, m_unmapped_swap.values())
375  used += (swapfile->size() >> 10) + 1;
376 
377  return (used >> 10);
378 }
379 
380 //***************************************************************************
382 {
383  QFileInfo file;
384  QString filename;
385 
386  // these 6 'X' chars are needed for mkstemp !
387  filename = _("kwave-swapfile-%1-XXXXXX");
388  filename = filename.arg(static_cast<unsigned int>(handle),
389  10, 10, QLatin1Char('0'));
390 
391  file.setFile(m_swap_dir, filename);
392  return file.absoluteFilePath();
393 }
394 
395 //***************************************************************************
397 {
398  // shortcut, if virtual memory is disabled
399  if (!m_virtual_limit)
400  return 0;
401 
402  // check for limits
403  quint64 limit = INT_MAX; // totalVirtual() in MB
404  if (m_virtual_limit < limit) limit = m_virtual_limit;
405  quint64 used = virtualUsed(); // in MB
406  quint64 available = (used < limit) ? (limit - used) : 0;
407  if ((size >> 20) >= available) {
408  qWarning("Kwave::MemoryManager::allocateVirtual(%u): out of memory, "\
409  "(used: %lluMB, available: %lluMB, limit=%lluMB)",
410  Kwave::toUint(size), used, available, limit);
411  dump("allocateVirtual");
412  return 0;
413  }
414 
415  // get a new handle
416  Kwave::Handle handle = newHandle();
417  if (!handle) return 0; // out of handles :-(
418 
419  // try to allocate
420  Kwave::SwapFile *swap = new Kwave::SwapFile(nextSwapFileName(handle));
421  Q_ASSERT(swap);
422  if (!swap) return 0; // out of memory :-(
423 
424  if (swap->allocate(size)) {
425  // succeeded, store the object in our map
426  m_unmapped_swap.insert(handle, swap);
427 #ifdef DEBUG_MEMORY
428  m_stats.swap.unmapped.bytes += size;
429  m_stats.swap.unmapped.handles++;
430  m_stats.swap.allocs++;
431 #endif /* DEBUG_MEMORY */
432  return handle;
433  } else {
434  qWarning("Kwave::MemoryManager::allocateVirtual(%u): OOM, "\
435  "(used: %lluMB) - failed resizing swap file",
436  Kwave::toUint(size), used);
437  // failed: give up, delete the swapfile object
438  delete swap;
439  }
440 
441  return 0;
442 }
443 
444 //***************************************************************************
446  size_t new_size)
447 {
448  // check: it must be in physical space, otherwise the rest makes no sense
449  Q_ASSERT(m_physical.contains(handle));
450  if (!m_physical.contains(handle)) return false;
451 
452  // get the old object in physical memory
453  physical_memory_t mem = m_physical[handle];
454  Q_ASSERT(mem.m_data);
455  Q_ASSERT(mem.m_size);
456  if (!mem.m_data || !mem.m_size) return false;
457 
458  // allocate a new object, including a new handle
459  // if successful it has been stored in m_unmapped_swap
460  Kwave::Handle temp_handle = allocateVirtual(new_size);
461  if (!temp_handle) return false;
462 
463  // copy old stuff to new location
464  Kwave::SwapFile *swap = m_unmapped_swap[temp_handle];
465  Q_ASSERT(swap);
466  swap->write(0, mem.m_data, Kwave::toUint(mem.m_size));
467 
468  // free the old physical memory
469  ::free(mem.m_data);
470  m_physical.remove(handle);
471 #ifdef DEBUG_MEMORY
472  m_stats.physical.handles--;
473  m_stats.physical.frees++;
474  m_stats.physical.bytes -= mem.m_size;
475 #endif /* DEBUG_MEMORY */
476 
477  // discard the new (temporary) handle and re-use the old one
478  m_unmapped_swap.remove(temp_handle); // temp_handle is now no longer valid
479  m_unmapped_swap.insert(handle, swap);
480 
481  // we now have the old data with new size and old handle in m_unmapped_swap
482 // qDebug("Kwave::MemoryManager[%9d] - moved to swap", handle);
483 
484  dump("convertToVirtual");
485  return true;
486 }
487 
488 //***************************************************************************
490  size_t new_size)
491 {
492  Q_ASSERT(new_size);
493  if (!new_size) return false;
494 
495  // check: it must be in physical space, otherwise the rest makes no sense
496  Q_ASSERT(m_unmapped_swap.contains(handle));
497  if (!m_unmapped_swap.contains(handle)) return false;
498  Kwave::SwapFile *swap = m_unmapped_swap[handle];
499  Q_ASSERT(swap);
500  if (!swap) return false;
501 
502  // allocate a new object, including a new handle
503  // if successful it has been stored in m_physical
504  Kwave::Handle temp_handle = allocatePhysical(new_size);
505  if (!temp_handle) return false;
506 
507  physical_memory_t mem = m_physical[temp_handle];
508  Q_ASSERT(mem.m_data);
509  Q_ASSERT(mem.m_size >= new_size);
510 
511  // copy old stuff to new location
512  if (new_size <= swap->size()) {
513  // shrinked
514  swap->read(0, mem.m_data, Kwave::toUint(new_size));
515  } else {
516  // grown
517  swap->read(0, mem.m_data, Kwave::toUint(swap->size()));
518  }
519 
520 #ifdef DEBUG_MEMORY
521  m_stats.swap.unmapped.bytes -= swap->size();
522  m_stats.swap.unmapped.handles--;
523  m_stats.swap.frees++;
524 #endif /* DEBUG_MEMORY */
525 
526  // free the old swapfile
527  m_unmapped_swap.remove(handle);
528  delete swap;
529 
530  // discard the new (temporary) handle and re-use the old one
531  m_physical.remove(temp_handle); // temp_handle is now no longer valid
532  m_physical.insert(handle, mem);
533 
534  // we now have the old data with new size and old handle in m_physical
535 // qDebug("Kwave::MemoryManager[%9d] - reloaded %2u MB from swap",
536 // handle, Kwave::toUint(mem.m_size >> 20));
537 
538  dump("convertToPhysical");
539  return true;
540 }
541 
542 //***************************************************************************
544 {
545  if (!handle) return;
546  if (m_physical.contains(handle)) return; // already ok
547  if (m_mapped_swap.contains(handle)) return; // not allowed
548  if (m_cached_swap.contains(handle)) return; // already fast enough
549 
550  Q_ASSERT(m_unmapped_swap.contains(handle));
551  if (!m_unmapped_swap.contains(handle)) return;
552 
553  const Kwave::SwapFile *swap = m_unmapped_swap[handle];
554  Q_ASSERT(swap);
555  if (!swap) return;
556 
557  size_t size = swap->size();
558 
559  quint64 limit = totalPhysical();
560  if (m_physical_limit < limit) limit = m_physical_limit;
561  quint64 used = physicalUsed();
562  quint64 available = (used < limit) ? (limit - used) : 0;
563 
564  // if we would go over the physical limit...
565  if ((size >> 20) >= available) {
566  // ...try to swap out some old stuff
567  if (!freePhysical(size)) return;
568  }
569 
570  // try to convert the swapfile back to physical RAM
571  convertToPhysical(handle, size);
572 }
573 
574 //***************************************************************************
576 {
577  QMutexLocker lock(&m_lock);
578 
579 // qDebug("Kwave::MemoryManager[%9d] - resize to %u", handle,
580 // Kwave::toUint(size));
581 
582  // case 1: physical memory
583  if (m_physical.contains(handle)) {
584  const physical_memory_t phys_c = m_physical[handle];
585 
586  // check: it must not be mmapped!
587  Q_ASSERT(!phys_c.m_mapcount);
588  if (phys_c.m_mapcount) return false;
589 
590  // if we are increasing: check if we get too large
591  size_t current_size = phys_c.m_size;
592  if ((size > current_size) && (physicalUsed() +
593  ((size - current_size) >> 20) > m_physical_limit))
594  {
595  // first try to swap out some old stuff
596  m_physical[handle].m_mapcount++;
597  bool physical_freed = freePhysical(size);
598  m_physical[handle].m_mapcount--;
599 
600  if (!physical_freed) {
601  // still too large -> move to virtual memory
602  if (m_virtual_limit) {
603  qDebug("Kwave::MemoryManager[%9d] - resize(%uMB) "
604  "-> moving to swap",
605  handle, Kwave::toUint(size >> 20));
606  return convertToVirtual(handle, size);
607  } else {
608  qDebug("Kwave::MemoryManager[%9d] - resize(%uMB) "
609  "-> OOM", handle, Kwave::toUint(size >> 20));
610  return false;
611  }
612  }
613  }
614 
615  // try to resize the physical memory
616  physical_memory_t phys = m_physical[handle];
617  void *old_block = phys.m_data;
618  void *new_block = ::realloc(old_block, size);
619  if (new_block) {
620  phys.m_data = new_block;
621  phys.m_size = size;
622  phys.m_mapcount = 0;
623  m_physical[handle] = phys;
624 #ifdef DEBUG_MEMORY
625  m_stats.physical.bytes -= current_size;
626  m_stats.physical.bytes += size;
627 #endif /* DEBUG_MEMORY */
628 
629  dump("resize");
630  return true;
631  } else {
632  // resizing failed, try to allocate virtual memory for it
633  return convertToVirtual(handle, size);
634  }
635  }
636 
637  // case 2: mapped swapfile in cache -> unmap !
638  unmapFromCache(handle); // make sure it is not in the cache
639 
640  // case 3: mapped swapfile -> forbidden !
641  Q_ASSERT(!m_mapped_swap.contains(handle));
642  if (m_mapped_swap.contains(handle))
643  return false;
644 
645  // case 4: unmapped swapfile -> resize
646  Q_ASSERT(m_unmapped_swap.contains(handle));
647  if (m_unmapped_swap.contains(handle)) {
648 
649  // try to find space in the physical memory
650  if ((physicalUsed() + (size >> 20) > m_physical_limit)) {
651  // free some space if necessary
652  if (freePhysical(size)) {
653  if (convertToPhysical(handle, size)) return true;
654  }
655  } else {
656  // try to convert into the currently available phys. RAM
657  if (convertToPhysical(handle, size)) return true;
658  }
659 
660  // not enough free RAM: resize the pagefile
661 // qDebug("Kwave::MemoryManager[%9d] - resize swap %u -> %u MB",
662 // handle,
663 // Kwave::toUint(swap->size() >> 20),
664 // Kwave::toUint(size >> 20));
665 
666  dump("resize");
667  Kwave::SwapFile *swap = m_unmapped_swap[handle];
668 #ifdef DEBUG_MEMORY
669  size_t old_size = swap->size();
670 #endif /* DEBUG_MEMORY */
671  bool ok = swap->resize(size);
672  if (!ok) return false;
673 #ifdef DEBUG_MEMORY
674  m_stats.swap.unmapped.bytes -= old_size;
675  m_stats.swap.unmapped.bytes += size;
676 #endif /* DEBUG_MEMORY */
677  return true;
678  }
679 
680  return false; // nothing known about this object / invalid handle?
681 }
682 
683 //***************************************************************************
685 {
686  if (!handle) return 0;
687  QMutexLocker lock(&m_lock);
688 
689  // case 1: physical memory
690  if (m_physical.contains(handle)) {
691  const physical_memory_t phys_c = m_physical[handle];
692  return phys_c.m_size;
693  }
694 
695  // case 2: cached mapped swapfile
696  if (m_cached_swap.contains(handle)) {
697  const Kwave::SwapFile *swapfile = m_cached_swap[handle];
698  return swapfile->size();
699  }
700 
701  // case 3: mapped swapfile
702  if (m_mapped_swap.contains(handle)) {
703  const Kwave::SwapFile *swapfile = m_mapped_swap[handle];
704  return swapfile->size();
705  }
706 
707  // case 4: unmapped swapfile
708  if (m_unmapped_swap.contains(handle)) {
709  const Kwave::SwapFile *swapfile = m_unmapped_swap[handle];
710  return swapfile->size();
711  }
712 
713  return 0;
714 }
715 
716 //***************************************************************************
718 {
719  if (!handle) return;
720  QMutexLocker lock(&m_lock);
721 
722 // qDebug("Kwave::MemoryManager[%9d] - free", handle);
723 
724  if (m_physical.contains(handle)) {
725  // physical memory (must not be mapped)
726  Q_ASSERT(!m_physical[handle].m_mapcount);
727 
728 #ifdef DEBUG_MEMORY
729  m_stats.physical.handles--;
730  m_stats.physical.frees++;
731  m_stats.physical.bytes -= m_physical[handle].m_size;
732 #endif /* DEBUG_MEMORY */
733 
734  void *b = m_physical[handle].m_data;
735  Q_ASSERT(b);
736  m_physical.remove(handle);
737  ::free(b);
738  handle = 0;
739 
740  dump("free");
741  return;
742  }
743 
744  Q_ASSERT(!m_mapped_swap.contains(handle));
745  if (m_mapped_swap.contains(handle)) {
746  // no-good: swapfile is still mapped !?
747  unmap(handle);
748  }
749 
750  unmapFromCache(handle); // make sure it is not in the cache
751 
752  if (m_unmapped_swap.contains(handle)) {
753  // remove the pagefile
754  Kwave::SwapFile *swap = m_unmapped_swap[handle];
755 #ifdef DEBUG_MEMORY
756  m_stats.swap.unmapped.handles--;
757  m_stats.swap.unmapped.bytes -= swap->size();
758  m_stats.swap.frees++;
759 #endif /* DEBUG_MEMORY */
760  m_unmapped_swap.remove(handle);
761  Q_ASSERT(!swap->mapCount());
762  delete swap;
763  handle = 0;
764 
765  dump("free");
766  return;
767  }
768 
769  Q_ASSERT(!handle);
770  handle = 0;
771 }
772 
773 //***************************************************************************
775 {
776  QMutexLocker lock(&m_lock);
777 
778  Q_ASSERT(handle);
779  if (!handle) return Q_NULLPTR; // object not found ?
780 
781  // try to convert to physical RAM
782  tryToMakePhysical(handle);
783 
784  // simple case: physical memory does not really need to be mapped
785  if (m_physical.contains(handle)) {
786  m_physical[handle].m_mapcount++;
787 // qDebug("Kwave::MemoryManager[%9d] - mmap -> physical", handle);
788  return m_physical[handle].m_data;
789  }
790 
791  // no physical mem -> must be a swapfile
792 
793  // if it is already in the cache -> shortcut !
794  if (m_cached_swap.contains(handle)) {
795  Kwave::SwapFile *swap = m_cached_swap[handle];
796  m_cached_swap.remove(handle);
797  m_mapped_swap.insert(handle, swap);
798 #ifdef DEBUG_MEMORY
799  m_stats.swap.cached.handles--;
800  m_stats.swap.cached.bytes -= swap->size();
801  m_stats.swap.mapped.handles++;
802  m_stats.swap.mapped.bytes += swap->size();
803 #endif /* DEBUG_MEMORY */
804 // qDebug("Kwave::MemoryManager[%9d] - mmap -> cache hit", handle);
805  Q_ASSERT(swap->mapCount() == 1);
806  return swap->address();
807  }
808 
809  // other simple case: already mapped
810  if (m_mapped_swap.contains(handle)) {
811  Kwave::SwapFile *swap = m_mapped_swap[handle];
812  Q_ASSERT(swap->mapCount() >= 1);
813 // qDebug("Kwave::MemoryManager[%9d] - mmap -> recursive(%d)",
814 // handle, swap->mapCount());
815  return swap->map(); // increase map count to 2...
816  }
817 
818  // more complicated case: unmapped swapfile
819  if (m_unmapped_swap.contains(handle)) {
820  // map it into memory
821  Kwave::SwapFile *swap = m_unmapped_swap[handle];
822  Q_ASSERT(!swap->mapCount());
823  void *mapped = swap->map();
824  if (!mapped) {
825  qDebug("Kwave::MemoryManager[%9d] - mmap FAILED", handle);
826  // maybe address space is already full wil already cached
827  // mapped swap files -> kick out the last one and try again
828  while (!mapped && !m_cached_swap.isEmpty()) {
829  Kwave::Handle h = m_cached_swap.keys().last();
830  unmapFromCache(h);
831  mapped = swap->map();
832  qDebug("Kwave::MemoryManager[%9d] - retry: %p", handle, mapped);
833  }
834 
835  if (!mapped) return Q_NULLPTR;
836  }
837 
838  // remember that we have mapped it, move the entry from the
839  // "unmapped_swap" to the "mapped_swap" list
840  m_unmapped_swap.remove(handle);
841  m_mapped_swap.insert(handle, swap);
842 
843 #ifdef DEBUG_MEMORY
844  m_stats.swap.unmapped.handles--;
845  m_stats.swap.unmapped.bytes -= swap->size();
846  m_stats.swap.mapped.handles++;
847  m_stats.swap.mapped.bytes += swap->size();
848 #endif /* DEBUG_MEMORY */
849 // qDebug("Kwave::MemoryManager[%9d] - mmap -> new mapping", handle);
850  return mapped;
851  } else {
852  Q_ASSERT(m_unmapped_swap.contains(handle));
853  }
854 
855  // nothing known about this object !?
856  return Q_NULLPTR;
857 }
858 
859 //***************************************************************************
861 {
862  if (m_cached_swap.contains(handle)) {
863 // qDebug("Kwave::MemoryManager[%9d] - unmapFromCache", handle);
864  Kwave::SwapFile *swap = m_cached_swap[handle];
865  Q_ASSERT(swap->mapCount() == 1);
866  swap->unmap();
867  Q_ASSERT(!swap->mapCount());
868  m_cached_swap.remove(handle);
869  m_unmapped_swap.insert(handle, swap);
870 #ifdef DEBUG_MEMORY
871  m_stats.swap.cached.handles--;
872  m_stats.swap.cached.bytes -= swap->size();
873  m_stats.swap.unmapped.handles++;
874  m_stats.swap.unmapped.bytes += swap->size();
875 #endif /* DEBUG_MEMORY */
876  }
877 
878  dump("unmap");
879 }
880 
881 //***************************************************************************
883 {
884  QMutexLocker lock(&m_lock);
885 
886  // simple case: physical memory does not really need to be unmapped
887  if (m_physical.contains(handle)) {
888 // qDebug("Kwave::MemoryManager[%9d] - unmap -> physical", handle);
889  Q_ASSERT(m_physical[handle].m_mapcount);
890  if (m_physical[handle].m_mapcount)
891  m_physical[handle].m_mapcount--;
892  return;
893  }
894 
895 // qDebug("Kwave::MemoryManager[%9d] - unmap swap", handle);
896 
897  // just to be sure: should also not be in cache!
898  Q_ASSERT(!m_cached_swap.contains(handle));
899  unmapFromCache(handle);
900 
901  // unmapped swapfile: already unmapped !?
902  if (m_unmapped_swap.contains(handle)) {
903  Q_ASSERT(!m_unmapped_swap.contains(handle));
904  return; // nothing to do
905  }
906 
907  // must be a mapped swapfile: move it into the cache
908  Q_ASSERT(m_mapped_swap.contains(handle));
909  if (m_mapped_swap.contains(handle)) {
910  Kwave::SwapFile *swap = m_mapped_swap[handle];
911  Q_ASSERT(swap->mapCount());
912  if (swap->mapCount() > 1) {
913  // only unmap and internally reduce the map count
914  swap->unmap();
915 // qDebug("Kwave::MemoryManager[%9d] - unmap -> recursive(%d)",
916 // handle, swap->mapCount());
917  } else if (swap->mapCount() == 1) {
918  // move to cache instead of really unmapping
919 
920  // make room in the cache if necessary
921  while (m_cached_swap.count() >= CACHE_SIZE) {
922  unmapFromCache(m_cached_swap.keys().first());
923  }
924 
925  // move it into the swap file cache
926  m_mapped_swap.remove(handle);
927  m_cached_swap.insert(handle, swap);
928 #ifdef DEBUG_MEMORY
929  m_stats.swap.mapped.handles--;
930  m_stats.swap.mapped.bytes -= swap->size();
931  m_stats.swap.cached.handles++;
932  m_stats.swap.cached.bytes += swap->size();
933 #endif /* DEBUG_MEMORY */
934 // qDebug("Kwave::MemoryManager[%9d] - unmap -> moved to cache",
935 // handle);
936  }
937  }
938 }
939 
940 //***************************************************************************
941 int Kwave::MemoryManager::readFrom(Kwave::Handle handle, unsigned int offset,
942  void *buffer, unsigned int length)
943 {
944  QMutexLocker lock(&m_lock);
945 
946  if (!handle) return 0;
947 
948  // try to convert to physical RAM
949  tryToMakePhysical(handle);
950 
951  // simple case: physical memory -> memcpy(...)
952  if (m_physical.contains(handle)) {
953 // qDebug("Kwave::MemoryManager[%9d] - readFrom -> physical", handle);
954  char *data = reinterpret_cast<char *>(m_physical[handle].m_data);
955  MEMCPY(buffer, data + offset, length);
956  return length;
957  }
958 
959  // no physical mem -> must be a swapfile
960 
961  // still in the cache and mapped -> memcpy(...)
962  if (m_cached_swap.contains(handle)) {
963  Kwave::SwapFile *swap = m_cached_swap[handle];
964  Q_ASSERT(swap->mapCount() == 1);
965  char *data = reinterpret_cast<char *>(swap->address());
966  Q_ASSERT(data);
967  if (!data) return 0;
968  MEMCPY(buffer, data + offset, length);
969 // qDebug("Kwave::MemoryManager[%9d] - readFrom -> cached swap", handle);
970  return length;
971  }
972 
973  // currently mmapped -> memcpy(...)
974  if (m_mapped_swap.contains(handle)) {
975  Kwave::SwapFile *swap = m_mapped_swap[handle];
976  Q_ASSERT(swap->mapCount() >= 1);
977  char *data = reinterpret_cast<char *>(swap->address());
978  Q_ASSERT(data);
979  if (!data) return 0;
980  MEMCPY(buffer, data + offset, length);
981 // qDebug("Kwave::MemoryManager[%9d] - readFrom -> mapped swap", handle);
982  return length;
983  }
984 
985  // now it must be in unmapped swap -> read(...)
986  Q_ASSERT(m_unmapped_swap.contains(handle));
987  if (m_unmapped_swap.contains(handle)) {
988 // qDebug("Kwave::MemoryManager[%9d] - readFrom -> unmapped swap", handle);
989  Kwave::SwapFile *swap = m_unmapped_swap[handle];
990  length = swap->read(offset, buffer, length);
991  return length;
992  }
993 
994  return 0;
995 }
996 
997 //***************************************************************************
998 int Kwave::MemoryManager::writeTo(Kwave::Handle handle, unsigned int offset,
999  const void *buffer, unsigned int length)
1000 {
1001  QMutexLocker lock(&m_lock);
1002 
1003  if (!handle) return 0;
1004 
1005  // try to convert to physical RAM
1006  tryToMakePhysical(handle);
1007 
1008  // simple case: memcpy to physical memory
1009  if (m_physical.contains(handle)) {
1010  physical_memory_t &mem = m_physical[handle];
1011 // qDebug("Kwave::MemoryManager[%9d] - writeTo -> physical", handle);
1012  char *data = reinterpret_cast<char *>(mem.m_data);
1013  Q_ASSERT(length <= mem.m_size);
1014  Q_ASSERT(offset < mem.m_size);
1015  Q_ASSERT(offset + length <= mem.m_size);
1016  MEMCPY(data + offset, buffer, length);
1017  return length;
1018  }
1019 
1020  // make sure it's not mmapped
1021  unmapFromCache(handle);
1022 
1023  // writing to mapped swap is not allowed
1024  Q_ASSERT(!m_mapped_swap.contains(handle));
1025  if (m_mapped_swap.contains(handle)) {
1026  return 0;
1027  }
1028 
1029  // now it must be in unmapped swap
1030  Q_ASSERT(m_unmapped_swap.contains(handle));
1031  if (m_unmapped_swap.contains(handle)) {
1032 // qDebug("Kwave::MemoryManager[%9d] - writeTo -> unmapped swap", handle);
1033  Kwave::SwapFile *swap = m_unmapped_swap[handle];
1034  swap->write(offset, buffer, length);
1035  return length;
1036  }
1037 
1038  return 0;
1039 }
1040 
1041 //***************************************************************************
1042 void Kwave::MemoryManager::dump(const char *function)
1043 {
1044 #if 0
1045  quint64 v_used = virtualUsed();
1046  quint64 p_used = physicalUsed();
1047 
1048  qDebug("------- %s -------", function);
1049  foreach (const Kwave::Handle &handle, m_physical.keys())
1050  qDebug(" P[%5u]: %5u", static_cast<unsigned int>(handle),
1051  m_physical[handle].m_size >> 20);
1052 
1053  unsigned int m = 0;
1054  foreach (const Kwave::Handle &handle, m_mapped_swap.keys()) {
1055  m += m_mapped_swap[handle]->size() >> 20;
1056  qDebug(" M[%5u]: %5u", static_cast<unsigned int>(handle),
1057  m_mapped_swap[handle]->size() >> 20);
1058  }
1059 
1060  unsigned int c = 0;
1061  foreach (const Kwave::Handle &handle, m_cached_swap.keys()) {
1062  c += m_cached_swap[handle]->size() >> 20;
1063  qDebug(" C[%5u]: %5u", static_cast<unsigned int>(handle),
1064  m_cached_swap[handle]->size() >> 20);
1065  }
1066 
1067  unsigned int u = 0;
1068  foreach (const Kwave::Handle &handle, m_unmapped_swap.keys()) {
1069  u += m_unmapped_swap[handle]->size() >> 20;
1070  qDebug(" U[%5u]: %5u", static_cast<unsigned int>(handle),
1071  m_unmapped_swap[handle]->size() >> 20);
1072  }
1073 
1074  qDebug("physical: %5llu MB, virtual: %5llu MB [m:%5u, c:%5u, u:%5u]",
1075  p_used, v_used, m, c, u);
1076 #endif
1077 
1078 #ifdef DEBUG_MEMORY
1079  qDebug("------- %s -------", function);
1080  qDebug("physical: %12llu, %12llu / %12llu (%12llu : %12llu)",
1081  m_stats.physical.handles,
1082  m_stats.physical.bytes,
1083  m_stats.physical.limit,
1084  m_stats.physical.allocs,
1085  m_stats.physical.frees);
1086  qDebug("mapped swap: %12llu, %12llu / %12llu (%12llu : %12llu)",
1087  m_stats.swap.mapped.handles,
1088  m_stats.swap.mapped.bytes,
1089  m_stats.swap.limit,
1090  m_stats.swap.allocs,
1091  m_stats.swap.frees);
1092  qDebug("cached: %12llu, %12llu",
1093  m_stats.swap.cached.handles,
1094  m_stats.swap.cached.bytes);
1095  qDebug("unmapped: %12llu, %12llu",
1096  m_stats.swap.unmapped.handles,
1097  m_stats.swap.unmapped.bytes);
1098  qDebug("-----------------------------------------------------------------");
1099 
1100 #else /* DEBUG_MEMORY */
1101  Q_UNUSED(function);
1102 #endif /* DEBUG_MEMORY */
1103 }
1104 
1105 //***************************************************************************
1106 //***************************************************************************
QHash< Kwave::Handle, Kwave::SwapFile * > m_unmapped_swap
void * map()
Definition: SwapFile.cpp:206
static Kwave::MemoryManager g_instance
void setVirtualLimit(quint64 mb) Q_DECL_EXPORT
quint64 totalPhysical() Q_DECL_EXPORT
void * address() const
Definition: SwapFile.h:58
int readFrom(Kwave::Handle handle, unsigned int offset, void *buffer, unsigned int length) Q_DECL_EXPORT
bool convertToVirtual(Kwave::Handle handle, size_t new_size)
bool resize(Kwave::Handle handle, size_t size) Q_DECL_EXPORT
size_t sizeOf(Kwave::Handle handle) Q_DECL_EXPORT
static MemoryManager & instance() Q_DECL_EXPORT
int write(unsigned int offset, const void *buffer, unsigned int length)
Definition: SwapFile.cpp:274
Kwave::Handle newHandle()
bool convertToPhysical(Kwave::Handle handle, size_t new_size)
bool freePhysical(size_t size)
QHash< Kwave::Handle, Kwave::SwapFile * > m_mapped_swap
void setUndoLimit(quint64 mb) Q_DECL_EXPORT
void tryToMakePhysical(Kwave::Handle handle)
void free(Kwave::Handle &handle) Q_DECL_EXPORT
void unmapFromCache(Kwave::Handle handle)
int Handle
Definition: MemoryManager.h:34
int read(unsigned int offset, void *buffer, unsigned int length)
Definition: SwapFile.cpp:258
bool allocate(size_t size)
Definition: SwapFile.cpp:65
void setSwapDirectory(const QString &dir) Q_DECL_EXPORT
#define MEMCPY
Definition: memcpy.h:37
QHash< Kwave::Handle, Kwave::SwapFile * > m_cached_swap
size_t size() const
Definition: SwapFile.h:64
void setPhysicalLimit(quint64 mb) Q_DECL_EXPORT
QString nextSwapFileName(Kwave::Handle handle)
Kwave::Handle allocatePhysical(size_t size)
quint64 undoLimit() const Q_DECL_EXPORT
static Kwave::Handle m_last_handle
#define _(m)
Definition: memcpy.c:66
void unmap(Kwave::Handle handle) Q_DECL_EXPORT
Kwave::Handle allocateVirtual(size_t size)
void * map(Kwave::Handle handle) Q_DECL_EXPORT
bool resize(size_t size)
Definition: SwapFile.cpp:122
#define CACHE_SIZE
int writeTo(Kwave::Handle handle, unsigned int offset, const void *buffer, unsigned int length) Q_DECL_EXPORT
Kwave::Handle allocate(size_t size) Q_DECL_EXPORT
unsigned int toUint(T x)
Definition: Utils.h:109
int mapCount() const
Definition: SwapFile.h:67
Kwave::LRU_Cache< Kwave::Handle, physical_memory_t > m_physical
void dump(const char *function)