Make Java measure thread-safe.
This commit is contained in:
@@ -26,6 +26,7 @@ function __transpileToJavaCommon(code) {
|
||||
.replace(/pos\[([^\]]+)\]/g, 'getPos($1)')
|
||||
.replace(/dim\[([^\]]+)\]/g, 'getDim($1)')
|
||||
.replace(/isUndefined/g, 'CSSConstants.isUndefined')
|
||||
.replace(/\/\*\(java\)!([^*]+)\*\//g, '$1')
|
||||
|
||||
// Since Java doesn't store its attributes in arrays, we need to use setters/getters to access
|
||||
// the appropriate layout/style fields
|
||||
|
@@ -378,6 +378,7 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth) {
|
||||
if (isRowUndefined || isColumnUndefined) {
|
||||
css_dim_t measureDim = node->measure(
|
||||
node->context,
|
||||
|
||||
width
|
||||
);
|
||||
if (isRowUndefined) {
|
||||
|
@@ -278,6 +278,7 @@ var computeLayout = (function() {
|
||||
if (isRowUndefined || isColumnUndefined) {
|
||||
var/*css_dim_t*/ measureDim = node.style.measure(
|
||||
/*(c)!node->context,*/
|
||||
/*(java)!layoutContext.measureOutput,*/
|
||||
width
|
||||
);
|
||||
if (isRowUndefined) {
|
||||
@@ -397,7 +398,7 @@ var computeLayout = (function() {
|
||||
|
||||
// This is the main recursive call. We layout non flexible children.
|
||||
if (alreadyComputedNextLayout === 0) {
|
||||
layoutNode(child, maxWidth);
|
||||
layoutNode(/*(java)!layoutContext, */child, maxWidth);
|
||||
}
|
||||
|
||||
// Absolute positioned elements do not take part of the layout, so we
|
||||
@@ -472,7 +473,7 @@ var computeLayout = (function() {
|
||||
}
|
||||
|
||||
// And we recursively call the layout algorithm for this child
|
||||
layoutNode(child, maxWidth);
|
||||
layoutNode(/*(java)!layoutContext, */child, maxWidth);
|
||||
}
|
||||
}
|
||||
|
||||
|
20
src/java/src/com/facebook/csslayout/CSSLayoutContext.java
Normal file
20
src/java/src/com/facebook/csslayout/CSSLayoutContext.java
Normal file
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* Copyright (c) 2014, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
package com.facebook.csslayout;
|
||||
|
||||
/**
|
||||
* A context for holding values local to a given instance of layout computation.
|
||||
*
|
||||
* This is necessary for making layout thread-safe. A separate instance should
|
||||
* be used when {@link CSSNode#calculateLayout} is called concurrently on
|
||||
* different node hierarchies.
|
||||
*/
|
||||
public class CSSLayoutContext {
|
||||
/*package*/ final MeasureOutput measureOutput = new MeasureOutput();
|
||||
}
|
@@ -39,10 +39,6 @@ public class CSSNode {
|
||||
UP_TO_DATE,
|
||||
}
|
||||
|
||||
// Only one copy kept around to keep from allocating a bunch of MeasureOutput objects
|
||||
// NOT THREAD SAFE! NOT RE-ENTRANT SAFE!
|
||||
private static final MeasureOutput MEASURE_OUTPUT = new MeasureOutput();
|
||||
|
||||
public static interface MeasureFunction {
|
||||
|
||||
/**
|
||||
@@ -114,22 +110,22 @@ public class CSSNode {
|
||||
return mMeasureFunction != null;
|
||||
}
|
||||
|
||||
/*package*/ MeasureOutput measure(float width) {
|
||||
/*package*/ MeasureOutput measure(MeasureOutput measureOutput, float width) {
|
||||
if (!isMeasureDefined()) {
|
||||
throw new RuntimeException("Measure function isn't defined!");
|
||||
}
|
||||
MEASURE_OUTPUT.height = CSSConstants.UNDEFINED;
|
||||
MEASURE_OUTPUT.width = CSSConstants.UNDEFINED;
|
||||
Assertions.assertNotNull(mMeasureFunction).measure(this, width, MEASURE_OUTPUT);
|
||||
return MEASURE_OUTPUT;
|
||||
measureOutput.height = CSSConstants.UNDEFINED;
|
||||
measureOutput.width = CSSConstants.UNDEFINED;
|
||||
Assertions.assertNotNull(mMeasureFunction).measure(this, width, measureOutput);
|
||||
return measureOutput;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the actual layout and saves the results in {@link #layout}
|
||||
*/
|
||||
public void calculateLayout() {
|
||||
public void calculateLayout(CSSLayoutContext layoutContext) {
|
||||
layout.resetResult();
|
||||
LayoutEngine.layoutNode(this, CSSConstants.UNDEFINED);
|
||||
LayoutEngine.layoutNode(layoutContext, this, CSSConstants.UNDEFINED);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -261,13 +261,16 @@ public class LayoutEngine {
|
||||
!FloatUtil.floatsEqual(node.lastLayout.parentMaxWidth, parentMaxWidth);
|
||||
}
|
||||
|
||||
/*package*/ static void layoutNode(CSSNode node, float parentMaxWidth) {
|
||||
/*package*/ static void layoutNode(
|
||||
CSSLayoutContext layoutContext,
|
||||
CSSNode node,
|
||||
float parentMaxWidth) {
|
||||
if (needsRelayout(node, parentMaxWidth)) {
|
||||
node.lastLayout.requestedWidth = node.layout.width;
|
||||
node.lastLayout.requestedHeight = node.layout.height;
|
||||
node.lastLayout.parentMaxWidth = parentMaxWidth;
|
||||
|
||||
layoutNodeImpl(node, parentMaxWidth);
|
||||
layoutNodeImpl(layoutContext, node, parentMaxWidth);
|
||||
node.lastLayout.copy(node.layout);
|
||||
} else {
|
||||
node.layout.copy(node.lastLayout);
|
||||
@@ -276,7 +279,10 @@ public class LayoutEngine {
|
||||
node.markHasNewLayout();
|
||||
}
|
||||
|
||||
private static void layoutNodeImpl(CSSNode node, float parentMaxWidth) {
|
||||
private static void layoutNodeImpl(
|
||||
CSSLayoutContext layoutContext,
|
||||
CSSNode node,
|
||||
float parentMaxWidth) {
|
||||
|
||||
for (int i = 0; i < node.getChildCount(); i++) {
|
||||
node.getChildAt(i).layout.resetResult();
|
||||
@@ -324,7 +330,8 @@ public class LayoutEngine {
|
||||
// Let's not measure the text if we already know both dimensions
|
||||
if (isRowUndefined || isColumnUndefined) {
|
||||
MeasureOutput measureDim = node.measure(
|
||||
width
|
||||
layoutContext.measureOutput,
|
||||
width
|
||||
);
|
||||
if (isRowUndefined) {
|
||||
node.layout.width = measureDim.width +
|
||||
@@ -443,7 +450,7 @@ public class LayoutEngine {
|
||||
|
||||
// This is the main recursive call. We layout non flexible children.
|
||||
if (alreadyComputedNextLayout == 0) {
|
||||
layoutNode(child, maxWidth);
|
||||
layoutNode(layoutContext, child, maxWidth);
|
||||
}
|
||||
|
||||
// Absolute positioned elements do not take part of the layout, so we
|
||||
@@ -518,7 +525,7 @@ public class LayoutEngine {
|
||||
}
|
||||
|
||||
// And we recursively call the layout algorithm for this child
|
||||
layoutNode(child, maxWidth);
|
||||
layoutNode(layoutContext, child, maxWidth);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -35,6 +35,7 @@ public class LayoutCachingTest {
|
||||
|
||||
@Test
|
||||
public void testCachesFullTree() {
|
||||
CSSLayoutContext layoutContext = new CSSLayoutContext();
|
||||
CSSNode root = new CSSNode();
|
||||
CSSNode c0 = new CSSNode();
|
||||
CSSNode c1 = new CSSNode();
|
||||
@@ -43,11 +44,11 @@ public class LayoutCachingTest {
|
||||
root.addChildAt(c1, 1);
|
||||
c0.addChildAt(c0c0, 0);
|
||||
|
||||
root.calculateLayout();
|
||||
root.calculateLayout(layoutContext);
|
||||
assertTreeHasNewLayout(true, root);
|
||||
markLayoutAppliedForTree(root);
|
||||
|
||||
root.calculateLayout();
|
||||
root.calculateLayout(layoutContext);
|
||||
assertTrue(root.hasNewLayout());
|
||||
assertTreeHasNewLayout(false, c0);
|
||||
assertTreeHasNewLayout(false, c1);
|
||||
@@ -55,6 +56,7 @@ public class LayoutCachingTest {
|
||||
|
||||
@Test
|
||||
public void testInvalidatesCacheWhenChildAdded() {
|
||||
CSSLayoutContext layoutContext = new CSSLayoutContext();
|
||||
CSSNode root = new CSSNode();
|
||||
CSSNode c0 = new CSSNode();
|
||||
CSSNode c1 = new CSSNode();
|
||||
@@ -68,12 +70,12 @@ public class LayoutCachingTest {
|
||||
c0.addChildAt(c0c0, 0);
|
||||
c0c0.addChildAt(c1c0, 0);
|
||||
|
||||
root.calculateLayout();
|
||||
root.calculateLayout(layoutContext);
|
||||
markLayoutAppliedForTree(root);
|
||||
|
||||
c0.addChildAt(c0c1, 1);
|
||||
|
||||
root.calculateLayout();
|
||||
root.calculateLayout(layoutContext);
|
||||
assertTrue(root.hasNewLayout());
|
||||
assertTrue(c0.hasNewLayout());
|
||||
assertTrue(c0c1.hasNewLayout());
|
||||
@@ -86,6 +88,7 @@ public class LayoutCachingTest {
|
||||
|
||||
@Test
|
||||
public void testInvalidatesCacheWhenEnumPropertyChanges() {
|
||||
CSSLayoutContext layoutContext = new CSSLayoutContext();
|
||||
CSSNode root = new CSSNode();
|
||||
CSSNode c0 = new CSSNode();
|
||||
CSSNode c1 = new CSSNode();
|
||||
@@ -94,11 +97,11 @@ public class LayoutCachingTest {
|
||||
root.addChildAt(c1, 1);
|
||||
c0.addChildAt(c0c0, 0);
|
||||
|
||||
root.calculateLayout();
|
||||
root.calculateLayout(layoutContext);
|
||||
markLayoutAppliedForTree(root);
|
||||
|
||||
c1.setAlignSelf(CSSAlign.CENTER);
|
||||
root.calculateLayout();
|
||||
root.calculateLayout(layoutContext);
|
||||
|
||||
assertTrue(root.hasNewLayout());
|
||||
assertTrue(c1.hasNewLayout());
|
||||
@@ -109,6 +112,7 @@ public class LayoutCachingTest {
|
||||
|
||||
@Test
|
||||
public void testInvalidatesCacheWhenFloatPropertyChanges() {
|
||||
CSSLayoutContext layoutContext = new CSSLayoutContext();
|
||||
CSSNode root = new CSSNode();
|
||||
CSSNode c0 = new CSSNode();
|
||||
CSSNode c1 = new CSSNode();
|
||||
@@ -117,11 +121,11 @@ public class LayoutCachingTest {
|
||||
root.addChildAt(c1, 1);
|
||||
c0.addChildAt(c0c0, 0);
|
||||
|
||||
root.calculateLayout();
|
||||
root.calculateLayout(layoutContext);
|
||||
markLayoutAppliedForTree(root);
|
||||
|
||||
c1.setMargin(Spacing.LEFT, 10);
|
||||
root.calculateLayout();
|
||||
root.calculateLayout(layoutContext);
|
||||
|
||||
assertTrue(root.hasNewLayout());
|
||||
assertTrue(c1.hasNewLayout());
|
||||
@@ -132,6 +136,7 @@ public class LayoutCachingTest {
|
||||
|
||||
@Test
|
||||
public void testInvalidatesFullTreeWhenParentWidthChanges() {
|
||||
CSSLayoutContext layoutContext = new CSSLayoutContext();
|
||||
CSSNode root = new CSSNode();
|
||||
CSSNode c0 = new CSSNode();
|
||||
CSSNode c1 = new CSSNode();
|
||||
@@ -142,11 +147,11 @@ public class LayoutCachingTest {
|
||||
c0.addChildAt(c0c0, 0);
|
||||
c1.addChildAt(c1c0, 0);
|
||||
|
||||
root.calculateLayout();
|
||||
root.calculateLayout(layoutContext);
|
||||
markLayoutAppliedForTree(root);
|
||||
|
||||
c0.setStyleWidth(200);
|
||||
root.calculateLayout();
|
||||
root.calculateLayout(layoutContext);
|
||||
|
||||
assertTrue(root.hasNewLayout());
|
||||
assertTrue(c0.hasNewLayout());
|
||||
@@ -158,6 +163,7 @@ public class LayoutCachingTest {
|
||||
|
||||
@Test
|
||||
public void testDoesNotInvalidateCacheWhenPropertyIsTheSame() {
|
||||
CSSLayoutContext layoutContext = new CSSLayoutContext();
|
||||
CSSNode root = new CSSNode();
|
||||
CSSNode c0 = new CSSNode();
|
||||
CSSNode c1 = new CSSNode();
|
||||
@@ -167,11 +173,11 @@ public class LayoutCachingTest {
|
||||
c0.addChildAt(c0c0, 0);
|
||||
root.setStyleWidth(200);
|
||||
|
||||
root.calculateLayout();
|
||||
root.calculateLayout(layoutContext);
|
||||
markLayoutAppliedForTree(root);
|
||||
|
||||
root.setStyleWidth(200);
|
||||
root.calculateLayout();
|
||||
root.calculateLayout(layoutContext);
|
||||
|
||||
assertTrue(root.hasNewLayout());
|
||||
assertTreeHasNewLayout(false, c0);
|
||||
@@ -180,6 +186,7 @@ public class LayoutCachingTest {
|
||||
|
||||
@Test
|
||||
public void testInvalidateCacheWhenHeightChangesPosition() {
|
||||
CSSLayoutContext layoutContext = new CSSLayoutContext();
|
||||
CSSNode root = new CSSNode();
|
||||
CSSNode c0 = new CSSNode();
|
||||
CSSNode c1 = new CSSNode();
|
||||
@@ -188,11 +195,11 @@ public class LayoutCachingTest {
|
||||
root.addChildAt(c1, 1);
|
||||
c1.addChildAt(c1c0, 0);
|
||||
|
||||
root.calculateLayout();
|
||||
root.calculateLayout(layoutContext);
|
||||
markLayoutAppliedForTree(root);
|
||||
|
||||
c0.setStyleHeight(100);
|
||||
root.calculateLayout();
|
||||
root.calculateLayout(layoutContext);
|
||||
|
||||
assertTrue(root.hasNewLayout());
|
||||
assertTrue(c0.hasNewLayout());
|
||||
@@ -202,6 +209,7 @@ public class LayoutCachingTest {
|
||||
|
||||
@Test
|
||||
public void testInvalidatesOnNewMeasureFunction() {
|
||||
CSSLayoutContext layoutContext = new CSSLayoutContext();
|
||||
CSSNode root = new CSSNode();
|
||||
CSSNode c0 = new CSSNode();
|
||||
CSSNode c1 = new CSSNode();
|
||||
@@ -210,7 +218,7 @@ public class LayoutCachingTest {
|
||||
root.addChildAt(c1, 1);
|
||||
c0.addChildAt(c0c0, 0);
|
||||
|
||||
root.calculateLayout();
|
||||
root.calculateLayout(layoutContext);
|
||||
markLayoutAppliedForTree(root);
|
||||
|
||||
c1.setMeasureFunction(new CSSNode.MeasureFunction() {
|
||||
@@ -221,7 +229,7 @@ public class LayoutCachingTest {
|
||||
}
|
||||
});
|
||||
|
||||
root.calculateLayout();
|
||||
root.calculateLayout(layoutContext);
|
||||
|
||||
assertTrue(root.hasNewLayout());
|
||||
assertTrue(c1.hasNewLayout());
|
||||
|
@@ -50,7 +50,8 @@ public class LayoutEngineTest {
|
||||
}
|
||||
|
||||
private static void test(String message, CSSNode style, CSSNode expectedLayout) {
|
||||
style.calculateLayout();
|
||||
CSSLayoutContext layoutContext = new CSSLayoutContext();
|
||||
style.calculateLayout(layoutContext);
|
||||
assertLayoutsEqual(message, style, expectedLayout);
|
||||
}
|
||||
|
||||
|
@@ -237,6 +237,7 @@ function transpileAnnotatedJStoC(jsCode) {
|
||||
.replace(/\n {2}/g, '\n')
|
||||
.replace(/\/\*\(c\)!([^*]+)\*\//g, '$1')
|
||||
.replace(/\/[*]!([^*]+)[*]\//g, '$1')
|
||||
.replace(/\/\*\(java\)!([^*]+)\*\//g, '')
|
||||
.split('\n').slice(1, -1).join('\n');
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user