Opera 12.15 Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

BSCache.cpp 8.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430
  1. /* -*- Mode: c++; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*-
  2. **
  3. ** Copyright (C) 1995-2011 Opera Software ASA. All rights reserved.
  4. **
  5. ** This file is part of the Opera web browser.
  6. ** It may not be distributed under any circumstances.
  7. */
  8. #include "core/pch.h"
  9. #ifdef SEARCH_ENGINE
  10. #include "modules/search_engine/BSCache.h"
  11. #include "modules/pi/OpSystemInfo.h"
  12. BSCache::BSCache(int max_cache) : m_storage()
  13. {
  14. m_head = NULL;
  15. m_memory_id = 1;
  16. m_branch_count = 0;
  17. m_cache_count = 0;
  18. m_journal_count = 0;
  19. m_flush_mode = ReleaseNUR;
  20. m_max_cache = max_cache;
  21. m_NUR_mark = 0;
  22. m_journal_flushed = FALSE;
  23. #ifdef _DEBUG
  24. branches_read = 0;
  25. branches_created = 0;
  26. branches_cached = 0;
  27. branches_written = 0;
  28. flush_count = 0;
  29. #endif
  30. }
  31. BSCache::~BSCache(void)
  32. {
  33. Item *b;
  34. OP_ASSERT(m_head == NULL);
  35. while (m_head != NULL)
  36. {
  37. b = m_head->previous;
  38. OP_DELETE(m_head);
  39. m_head = b;
  40. }
  41. }
  42. OP_STATUS BSCache::Flush(ReleaseSeverity severity, int max_ms)
  43. {
  44. Item *b, *next, *tmp;
  45. BSCache::Item::DiskId new_id;
  46. double time_limit;
  47. int counter;
  48. #ifdef _DEBUG
  49. ++flush_count;
  50. #endif
  51. time_limit = g_op_time_info->GetWallClockMS() + max_ms;
  52. m_journal_count = 0;
  53. if (m_storage.InTransaction() && !m_journal_flushed)
  54. {
  55. counter = 0;
  56. b = m_head;
  57. while (b != NULL)
  58. {
  59. ++m_journal_count;
  60. if (!b->journalled)
  61. {
  62. if (b->modified && b->disk_id > 0)
  63. {
  64. RETURN_IF_ERROR(m_storage.PreJournal(((OpFileLength)b->disk_id) * m_storage.GetBlockSize()));
  65. b->journalled = TRUE;
  66. }
  67. else if (b->disk_id == 0 && b->deleted_id > 0)
  68. {
  69. RETURN_IF_ERROR(m_storage.PreJournal(((OpFileLength)b->deleted_id) * m_storage.GetBlockSize()));
  70. b->journalled = TRUE;
  71. }
  72. else if (b->modified && b->disk_id < 0 && severity == JournalAll)
  73. {
  74. new_id = (BSCache::Item::DiskId)(m_storage.Reserve() / m_storage.GetBlockSize());
  75. if (new_id == 0)
  76. return OpStatus::ERR_NO_DISK;
  77. b->id_reserved = TRUE;
  78. b->modified = TRUE;
  79. b->OnIdChange(new_id, b->disk_id);
  80. b->disk_id = new_id;
  81. b->journalled = TRUE;
  82. }
  83. if (counter < 9)
  84. ++counter;
  85. else
  86. {
  87. counter = 0;
  88. if (max_ms > 0 && g_op_time_info->GetWallClockMS() >= time_limit)
  89. return OpBoolean::IS_FALSE;
  90. }
  91. }
  92. b = b->previous;
  93. }
  94. if (severity == JournalOnly || severity == JournalAll)
  95. return OpBoolean::IS_TRUE;
  96. RETURN_IF_ERROR(m_storage.FlushJournal());
  97. m_journal_flushed = TRUE;
  98. }
  99. counter = 0;
  100. next = NULL;
  101. b = m_head;
  102. while (b != NULL)
  103. {
  104. if (b->modified)
  105. {
  106. RETURN_IF_ERROR(b->Flush(&m_storage));
  107. if (b->journalled)
  108. {
  109. b->journalled = FALSE;
  110. --m_journal_count;
  111. }
  112. #ifdef _DEBUG
  113. ++branches_written;
  114. #endif
  115. }
  116. if (b->disk_id == 0 && b->deleted_id > 0)
  117. {
  118. if (b->id_reserved)
  119. {
  120. if (!m_storage.Write(b, 0, (OpFileLength)(((OpFileLength)b->deleted_id) * m_storage.GetBlockSize())))
  121. return OpStatus::ERR_NO_DISK;
  122. b->id_reserved = FALSE;
  123. }
  124. if (b->journalled)
  125. {
  126. b->journalled = FALSE;
  127. --m_journal_count;
  128. }
  129. if (!m_storage.Delete(((OpFileLength)b->deleted_id) * m_storage.GetBlockSize()))
  130. return OpStatus::ERR_NO_DISK;
  131. b->deleted_id = 0;
  132. }
  133. tmp = b->previous;
  134. if (b->reference_count == 0 &&
  135. (b->disk_id == 0 || severity == ReleaseAll ||
  136. (severity == ReleaseNUR && b->NUR_mark != m_NUR_mark && b->NUR_mark != ((m_NUR_mark + NUR_MAX) & NUR_MASK))))
  137. {
  138. if (next == NULL)
  139. m_head = b->previous;
  140. else
  141. next->previous = b->previous;
  142. --m_cache_count;
  143. --m_branch_count;
  144. OP_DELETE(b);
  145. }
  146. else
  147. next = b;
  148. if (counter < 9)
  149. ++counter;
  150. else
  151. {
  152. counter = 0;
  153. if (max_ms > 0 && g_op_time_info->GetWallClockMS() >= time_limit)
  154. return OpBoolean::IS_FALSE;
  155. }
  156. b = tmp;
  157. }
  158. m_journal_count = 0;
  159. m_memory_id = 1;
  160. m_journal_flushed = FALSE;
  161. return OpBoolean::IS_TRUE;
  162. }
  163. #ifdef ESTIMATE_MEMORY_USED_AVAILABLE
  164. size_t BSCache::EstimateMemoryUsed(void) const
  165. {
  166. size_t sum = 0;
  167. Item *b;
  168. for (b=m_head; b; b=b->previous)
  169. sum += b->EstimateMemoryUsed() + 2*sizeof(size_t);
  170. return sum +
  171. sizeof(m_head) +
  172. sizeof(m_branch_count) +
  173. sizeof(m_cache_count) +
  174. sizeof(m_journal_count) +
  175. sizeof(m_max_cache) +
  176. m_storage.EstimateMemoryUsed() +
  177. sizeof(m_memory_id) +
  178. sizeof(m_NUR_mark) +
  179. sizeof(m_flush_mode) +
  180. sizeof(m_journal_flushed);
  181. }
  182. size_t BSCache::Item::EstimateMemoryUsed() const
  183. {
  184. return
  185. sizeof(disk_id) +
  186. sizeof(previous) +
  187. sizeof(modified) +
  188. sizeof(in_list) +
  189. sizeof(journalled) +
  190. sizeof(deleted_id) +
  191. sizeof(reference_count) +
  192. sizeof(NUR_mark) +
  193. sizeof(id_reserved);
  194. }
  195. #endif
  196. void BSCache::ClearCache(void)
  197. {
  198. Item *b;
  199. while (m_head != NULL)
  200. {
  201. OP_ASSERT(m_head->reference_count == 0);
  202. b = m_head->previous;
  203. OP_DELETE(m_head);
  204. m_head = b;
  205. }
  206. m_branch_count = 0;
  207. m_cache_count = 0;
  208. m_journal_count = 0;
  209. m_memory_id = 1;
  210. }
  211. OP_STATUS BSCache::Create(Item **t, Item *reference, int ref_node)
  212. {
  213. int journal_dec;
  214. journal_dec = m_flush_mode == JournalOnly ? m_journal_count : 0;
  215. if (m_cache_count - journal_dec > m_max_cache || (m_branch_count - journal_dec > m_max_cache * 2 && m_cache_count > 0))
  216. {
  217. RETURN_IF_ERROR(Flush(m_flush_mode));
  218. if (m_cache_count > m_max_cache || (m_branch_count > m_max_cache * 2 && m_cache_count > 0))
  219. {
  220. m_NUR_mark = (m_NUR_mark + 1) & NUR_MASK;
  221. }
  222. }
  223. if (reference != NULL && !reference->in_list)
  224. {
  225. reference->previous = m_head;
  226. m_head = reference;
  227. reference->in_list = TRUE;
  228. }
  229. if ((*t = NewMemoryItem((int)m_memory_id, reference, ref_node, m_NUR_mark)) == NULL)
  230. {
  231. if (m_cache_count > 0)
  232. {
  233. OP_STATUS err;
  234. if (OpStatus::IsError(err = Flush(ReleaseAll)))
  235. {
  236. OP_DELETE(*t);
  237. return err;
  238. }
  239. if ((*t = NewMemoryItem((int)m_memory_id, reference, ref_node, m_NUR_mark)) == NULL)
  240. return OpStatus::ERR_NO_MEMORY;
  241. }
  242. else return OpStatus::ERR_NO_MEMORY;
  243. }
  244. (*t)->previous = m_head;
  245. m_head = *t;
  246. (*t)->in_list = TRUE;
  247. ++m_memory_id;
  248. (*t)->reference_count = 1;
  249. #ifdef _DEBUG
  250. ++branches_created;
  251. #endif
  252. ++m_branch_count;
  253. return OpStatus::OK;
  254. }
  255. void BSCache::Unlink(Item *t)
  256. {
  257. t->modified = FALSE; // will be deleted on the next Flush()
  258. t->deleted_id = t->disk_id;
  259. t->disk_id = 0;
  260. }
  261. OP_STATUS BSCache::Load(Item **t, OpFileLength id)
  262. {
  263. Item *tmp;
  264. OP_STATUS err;
  265. int journal_dec;
  266. journal_dec = m_flush_mode == JournalOnly ? m_journal_count : 0;
  267. for (tmp = m_head; tmp != NULL; tmp = tmp->previous)
  268. if (tmp->disk_id == (BSCache::Item::DiskId)id)
  269. {
  270. #ifdef _DEBUG
  271. ++branches_cached;
  272. #endif
  273. if (tmp->reference_count == 0)
  274. --m_cache_count;
  275. ++(tmp->reference_count);
  276. tmp->NUR_mark = m_NUR_mark;
  277. *t = tmp;
  278. return OpStatus::OK;
  279. }
  280. if (m_cache_count - journal_dec > m_max_cache || (m_branch_count - journal_dec > m_max_cache * 2 && m_cache_count > 0))
  281. {
  282. RETURN_IF_ERROR(Flush(m_flush_mode));
  283. if (m_cache_count > m_max_cache || (m_branch_count > m_max_cache * 2 && m_cache_count > 0))
  284. {
  285. m_NUR_mark = (m_NUR_mark + 1) & NUR_MASK;
  286. }
  287. }
  288. if ((*t = NewDiskItem(id, m_NUR_mark)) == NULL)
  289. {
  290. if (m_cache_count > 0)
  291. {
  292. if (OpStatus::IsError(err = Flush(ReleaseAll)))
  293. {
  294. OP_DELETE(*t);
  295. return err;
  296. }
  297. if ((*t = NewDiskItem(id, m_NUR_mark)) == NULL)
  298. return OpStatus::ERR_NO_MEMORY;
  299. }
  300. else return OpStatus::ERR_NO_MEMORY;
  301. }
  302. if (OpStatus::IsError(err = (*t)->Read(&m_storage)))
  303. {
  304. OP_DELETE(*t);
  305. *t = NULL;
  306. return err;
  307. }
  308. (*t)->previous = m_head;
  309. m_head = *t;
  310. (*t)->in_list = TRUE;
  311. ++m_memory_id;
  312. (*t)->reference_count = 1;
  313. #ifdef _DEBUG
  314. ++branches_read;
  315. #endif
  316. ++m_branch_count;
  317. return OpStatus::OK;
  318. }
  319. void BSCache::Load(Item **t, Item *b)
  320. {
  321. *t = b;
  322. #ifdef _DEBUG
  323. ++branches_cached;
  324. #endif
  325. OP_ASSERT((*t)->disk_id != 0); // deleted branch
  326. if ((*t)->reference_count == 0)
  327. --m_cache_count;
  328. ++((*t)->reference_count);
  329. (*t)->NUR_mark = m_NUR_mark;
  330. }
  331. void BSCache::Release(Item *t)
  332. {
  333. OP_ASSERT(t->reference_count > 0);
  334. if (--(t->reference_count) > 0)
  335. return;
  336. if (t->reference_count < 0)
  337. {
  338. // just to fix what must never happen
  339. t->reference_count = 0;
  340. }
  341. ++m_cache_count;
  342. if (!t->in_list)
  343. {
  344. t->previous = m_head;
  345. t->in_list = TRUE;
  346. m_head = t;
  347. }
  348. }
  349. #endif // SEARCH_ENGINE