SlotVisitor.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371
  1. #include "config.h"
  2. #include "SlotVisitor.h"
  3. #include "SlotVisitorInlines.h"
  4. #include "ConservativeRoots.h"
  5. #include "CopiedSpace.h"
  6. #include "CopiedSpaceInlines.h"
  7. #include "GCThread.h"
  8. #include "JSArray.h"
  9. #include "JSDestructibleObject.h"
  10. #include "VM.h"
  11. #include "JSObject.h"
  12. #include "JSString.h"
  13. #include "Operations.h"
  14. #include <wtf/StackStats.h>
  15. namespace JSC {
  16. SlotVisitor::SlotVisitor(GCThreadSharedData& shared)
  17. : m_stack(shared.m_vm->heap.blockAllocator())
  18. , m_visitCount(0)
  19. , m_isInParallelMode(false)
  20. , m_shared(shared)
  21. , m_shouldHashCons(false)
  22. #if !ASSERT_DISABLED
  23. , m_isCheckingForDefaultMarkViolation(false)
  24. , m_isDraining(false)
  25. #endif
  26. {
  27. }
  28. SlotVisitor::~SlotVisitor()
  29. {
  30. ASSERT(m_stack.isEmpty());
  31. }
  32. void SlotVisitor::setup()
  33. {
  34. m_shared.m_shouldHashCons = m_shared.m_vm->haveEnoughNewStringsToHashCons();
  35. m_shouldHashCons = m_shared.m_shouldHashCons;
  36. #if ENABLE(PARALLEL_GC)
  37. for (unsigned i = 0; i < m_shared.m_gcThreads.size(); ++i)
  38. m_shared.m_gcThreads[i]->slotVisitor()->m_shouldHashCons = m_shared.m_shouldHashCons;
  39. #endif
  40. }
  41. void SlotVisitor::reset()
  42. {
  43. m_visitCount = 0;
  44. ASSERT(m_stack.isEmpty());
  45. #if ENABLE(PARALLEL_GC)
  46. ASSERT(m_opaqueRoots.isEmpty()); // Should have merged by now.
  47. #else
  48. m_opaqueRoots.clear();
  49. #endif
  50. if (m_shouldHashCons) {
  51. m_uniqueStrings.clear();
  52. m_shouldHashCons = false;
  53. }
  54. }
  55. void SlotVisitor::append(ConservativeRoots& conservativeRoots)
  56. {
  57. StackStats::probe();
  58. JSCell** roots = conservativeRoots.roots();
  59. size_t size = conservativeRoots.size();
  60. for (size_t i = 0; i < size; ++i)
  61. internalAppend(roots[i]);
  62. }
  63. ALWAYS_INLINE static void visitChildren(SlotVisitor& visitor, const JSCell* cell)
  64. {
  65. StackStats::probe();
  66. #if ENABLE(SIMPLE_HEAP_PROFILING)
  67. m_visitedTypeCounts.count(cell);
  68. #endif
  69. ASSERT(Heap::isMarked(cell));
  70. if (isJSString(cell)) {
  71. JSString::visitChildren(const_cast<JSCell*>(cell), visitor);
  72. return;
  73. }
  74. if (isJSFinalObject(cell)) {
  75. JSFinalObject::visitChildren(const_cast<JSCell*>(cell), visitor);
  76. return;
  77. }
  78. if (isJSArray(cell)) {
  79. JSArray::visitChildren(const_cast<JSCell*>(cell), visitor);
  80. return;
  81. }
  82. cell->methodTable()->visitChildren(const_cast<JSCell*>(cell), visitor);
  83. }
  84. void SlotVisitor::donateKnownParallel()
  85. {
  86. StackStats::probe();
  87. // NOTE: Because we re-try often, we can afford to be conservative, and
  88. // assume that donating is not profitable.
  89. // Avoid locking when a thread reaches a dead end in the object graph.
  90. if (m_stack.size() < 2)
  91. return;
  92. // If there's already some shared work queued up, be conservative and assume
  93. // that donating more is not profitable.
  94. if (m_shared.m_sharedMarkStack.size())
  95. return;
  96. // If we're contending on the lock, be conservative and assume that another
  97. // thread is already donating.
  98. MutexTryLocker locker(m_shared.m_markingLock);
  99. if (!locker.locked())
  100. return;
  101. // Otherwise, assume that a thread will go idle soon, and donate.
  102. m_stack.donateSomeCellsTo(m_shared.m_sharedMarkStack);
  103. if (m_shared.m_numberOfActiveParallelMarkers < Options::numberOfGCMarkers())
  104. m_shared.m_markingCondition.broadcast();
  105. }
  106. void SlotVisitor::drain()
  107. {
  108. StackStats::probe();
  109. ASSERT(m_isInParallelMode);
  110. #if ENABLE(PARALLEL_GC)
  111. if (Options::numberOfGCMarkers() > 1) {
  112. while (!m_stack.isEmpty()) {
  113. m_stack.refill();
  114. for (unsigned countdown = Options::minimumNumberOfScansBetweenRebalance(); m_stack.canRemoveLast() && countdown--;)
  115. visitChildren(*this, m_stack.removeLast());
  116. donateKnownParallel();
  117. }
  118. mergeOpaqueRootsIfNecessary();
  119. return;
  120. }
  121. #endif
  122. while (!m_stack.isEmpty()) {
  123. m_stack.refill();
  124. while (m_stack.canRemoveLast())
  125. visitChildren(*this, m_stack.removeLast());
  126. }
  127. }
  128. void SlotVisitor::drainFromShared(SharedDrainMode sharedDrainMode)
  129. {
  130. StackStats::probe();
  131. ASSERT(m_isInParallelMode);
  132. ASSERT(Options::numberOfGCMarkers());
  133. bool shouldBeParallel;
  134. #if ENABLE(PARALLEL_GC)
  135. shouldBeParallel = Options::numberOfGCMarkers() > 1;
  136. #else
  137. ASSERT(Options::numberOfGCMarkers() == 1);
  138. shouldBeParallel = false;
  139. #endif
  140. if (!shouldBeParallel) {
  141. // This call should be a no-op.
  142. ASSERT_UNUSED(sharedDrainMode, sharedDrainMode == MasterDrain);
  143. ASSERT(m_stack.isEmpty());
  144. ASSERT(m_shared.m_sharedMarkStack.isEmpty());
  145. return;
  146. }
  147. #if ENABLE(PARALLEL_GC)
  148. {
  149. MutexLocker locker(m_shared.m_markingLock);
  150. m_shared.m_numberOfActiveParallelMarkers++;
  151. }
  152. while (true) {
  153. {
  154. MutexLocker locker(m_shared.m_markingLock);
  155. m_shared.m_numberOfActiveParallelMarkers--;
  156. // How we wait differs depending on drain mode.
  157. if (sharedDrainMode == MasterDrain) {
  158. // Wait until either termination is reached, or until there is some work
  159. // for us to do.
  160. while (true) {
  161. // Did we reach termination?
  162. if (!m_shared.m_numberOfActiveParallelMarkers && m_shared.m_sharedMarkStack.isEmpty()) {
  163. // Let any sleeping slaves know it's time for them to return;
  164. m_shared.m_markingCondition.broadcast();
  165. return;
  166. }
  167. // Is there work to be done?
  168. if (!m_shared.m_sharedMarkStack.isEmpty())
  169. break;
  170. // Otherwise wait.
  171. m_shared.m_markingCondition.wait(m_shared.m_markingLock);
  172. }
  173. } else {
  174. ASSERT(sharedDrainMode == SlaveDrain);
  175. // Did we detect termination? If so, let the master know.
  176. if (!m_shared.m_numberOfActiveParallelMarkers && m_shared.m_sharedMarkStack.isEmpty())
  177. m_shared.m_markingCondition.broadcast();
  178. while (m_shared.m_sharedMarkStack.isEmpty() && !m_shared.m_parallelMarkersShouldExit)
  179. m_shared.m_markingCondition.wait(m_shared.m_markingLock);
  180. // Is the current phase done? If so, return from this function.
  181. if (m_shared.m_parallelMarkersShouldExit)
  182. return;
  183. }
  184. size_t idleThreadCount = Options::numberOfGCMarkers() - m_shared.m_numberOfActiveParallelMarkers;
  185. m_stack.stealSomeCellsFrom(m_shared.m_sharedMarkStack, idleThreadCount);
  186. m_shared.m_numberOfActiveParallelMarkers++;
  187. }
  188. drain();
  189. }
  190. #endif
  191. }
  192. void SlotVisitor::mergeOpaqueRoots()
  193. {
  194. StackStats::probe();
  195. ASSERT(!m_opaqueRoots.isEmpty()); // Should only be called when opaque roots are non-empty.
  196. {
  197. MutexLocker locker(m_shared.m_opaqueRootsLock);
  198. HashSet<void*>::iterator begin = m_opaqueRoots.begin();
  199. HashSet<void*>::iterator end = m_opaqueRoots.end();
  200. for (HashSet<void*>::iterator iter = begin; iter != end; ++iter)
  201. m_shared.m_opaqueRoots.add(*iter);
  202. }
  203. m_opaqueRoots.clear();
  204. }
  205. ALWAYS_INLINE bool JSString::tryHashConsLock()
  206. {
  207. #if ENABLE(PARALLEL_GC)
  208. unsigned currentFlags = m_flags;
  209. if (currentFlags & HashConsLock)
  210. return false;
  211. unsigned newFlags = currentFlags | HashConsLock;
  212. if (!WTF::weakCompareAndSwap(&m_flags, currentFlags, newFlags))
  213. return false;
  214. WTF::memoryBarrierAfterLock();
  215. return true;
  216. #else
  217. if (isHashConsSingleton())
  218. return false;
  219. m_flags |= HashConsLock;
  220. return true;
  221. #endif
  222. }
  223. ALWAYS_INLINE void JSString::releaseHashConsLock()
  224. {
  225. #if ENABLE(PARALLEL_GC)
  226. WTF::memoryBarrierBeforeUnlock();
  227. #endif
  228. m_flags &= ~HashConsLock;
  229. }
  230. ALWAYS_INLINE bool JSString::shouldTryHashCons()
  231. {
  232. return ((length() > 1) && !isRope() && !isHashConsSingleton());
  233. }
  234. ALWAYS_INLINE void SlotVisitor::internalAppend(JSValue* slot)
  235. {
  236. // This internalAppend is only intended for visits to object and array backing stores.
  237. // as it can change the JSValue pointed to be the argument when the original JSValue
  238. // is a string that contains the same contents as another string.
  239. StackStats::probe();
  240. ASSERT(slot);
  241. JSValue value = *slot;
  242. ASSERT(value);
  243. if (!value.isCell())
  244. return;
  245. JSCell* cell = value.asCell();
  246. if (!cell)
  247. return;
  248. validate(cell);
  249. if (m_shouldHashCons && cell->isString()) {
  250. JSString* string = jsCast<JSString*>(cell);
  251. if (string->shouldTryHashCons() && string->tryHashConsLock()) {
  252. UniqueStringMap::AddResult addResult = m_uniqueStrings.add(string->string().impl(), value);
  253. if (addResult.isNewEntry)
  254. string->setHashConsSingleton();
  255. else {
  256. JSValue existingJSValue = addResult.iterator->value;
  257. if (value != existingJSValue)
  258. jsCast<JSString*>(existingJSValue.asCell())->clearHashConsSingleton();
  259. *slot = existingJSValue;
  260. string->releaseHashConsLock();
  261. return;
  262. }
  263. string->releaseHashConsLock();
  264. }
  265. }
  266. internalAppend(cell);
  267. }
  268. void SlotVisitor::harvestWeakReferences()
  269. {
  270. StackStats::probe();
  271. for (WeakReferenceHarvester* current = m_shared.m_weakReferenceHarvesters.head(); current; current = current->next())
  272. current->visitWeakReferences(*this);
  273. }
  274. void SlotVisitor::finalizeUnconditionalFinalizers()
  275. {
  276. StackStats::probe();
  277. while (m_shared.m_unconditionalFinalizers.hasNext())
  278. m_shared.m_unconditionalFinalizers.removeNext()->finalizeUnconditionally();
  279. }
  280. #if ENABLE(GC_VALIDATION)
  281. void SlotVisitor::validate(JSCell* cell)
  282. {
  283. RELEASE_ASSERT(cell);
  284. if (!cell->structure()) {
  285. dataLogF("cell at %p has a null structure\n" , cell);
  286. CRASH();
  287. }
  288. // Both the cell's structure, and the cell's structure's structure should be the Structure Structure.
  289. // I hate this sentence.
  290. if (cell->structure()->structure()->JSCell::classInfo() != cell->structure()->JSCell::classInfo()) {
  291. const char* parentClassName = 0;
  292. const char* ourClassName = 0;
  293. if (cell->structure()->structure() && cell->structure()->structure()->JSCell::classInfo())
  294. parentClassName = cell->structure()->structure()->JSCell::classInfo()->className;
  295. if (cell->structure()->JSCell::classInfo())
  296. ourClassName = cell->structure()->JSCell::classInfo()->className;
  297. dataLogF("parent structure (%p <%s>) of cell at %p doesn't match cell's structure (%p <%s>)\n",
  298. cell->structure()->structure(), parentClassName, cell, cell->structure(), ourClassName);
  299. CRASH();
  300. }
  301. // Make sure we can walk the ClassInfo chain
  302. const ClassInfo* info = cell->classInfo();
  303. do { } while ((info = info->parentClass));
  304. }
  305. #else
  306. void SlotVisitor::validate(JSCell*)
  307. {
  308. }
  309. #endif
  310. } // namespace JSC